πŸ“‘Template in C++

General Purpose Functions:

General purpose functions are functions that can be used for a variety of purposes, rather than being specific to a particular task or data type. They are typically designed to be reusable and flexible, and can be used in a wide range of applications.

Example:

#include <iostream>

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 1, y = 2;
    std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
    swap(x, y);
    std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
    return 0;
}

In this example, we define a general purpose function called swap that can be used to swap the values of two variables of any type. The function takes two references to a template parameter T, which can be any type, and swaps their values using a temporary variable. We then use this function to swap the values of two int variables in the main function.

Macros:

Macros are preprocessor directives that are used to define constants, functions, or other constructs that can be used throughout a program. They are typically defined using the #define directive, and can contain parameters that are replaced with their values at compile time.

Example:

#include <iostream>

#define PI 3.14159

int main() {
    double radius = 5.0;
    double area = PI * radius * radius;
    std::cout << "Radius: " << radius << ", Area: " << area << std::endl;
    return 0;
}

In this example, we define a macro called PI that represents the value of pi, and use it to calculate the area of a circle with a given radius. The macro is defined using the #define directive, and is used in a mathematical expression in the main function.

Function Templates:

Function templates are functions that can be used to define a family of functions that operate on different types or values. They are defined using the template keyword followed by a template parameter list, which specifies one or more template parameters that can be used to define the types or values that the function operates on.

Example:

#include <iostream>

template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

int main() {
    int x = 1, y = 2;
    double a = 3.14, b = 2.71;
    std::cout << "Max of " << x << " and " << y << " is " << max(x, y) << std::endl;
    std::cout << "Max of " << a << " and " << b << " is " << max(a, b) << std::endl;
    return 0;
}

In this example, we define a function template called max that can be used to find the maximum of two values of any type. The function takes two parameters of type T, which is a template parameter that can be any type. We then use this function template to find the maximum of two int variables and two double variables in the main function.

Template Parameters:

Template parameters are parameters that are used to define the types or values that a function or class template operates on. They are specified in a template parameter list within angle brackets (<>), and can be of several different types, including type parameters, non-type parameters, and template parameters.

Example:

template <typename T, int N>
class Array {
public:
    Array() : data(new T[N]) {}
    ~Array() { delete[] data; }
private:
    T* data;
};

In this example, we define a class template called Array that represents a fixed-size array of elements of type T. The template has two template parameters: T, which is a type parameter that specifies the type of the elements in the array, and N, which is a non-type parameter that specifies the size of the array. The Array class uses these template parameters to allocate and deallocate the memory for the array.

Template Parameter Conversion:

Template parameter conversion is the process of converting a template parameter to a different type or value. This is typically done using a type or value conversion operator, which is a function that is defined in the template class or function and is used to convert the template parameter to the desired type or value.

Example:

template <typename T>
class MyClass {
public:
    operator int() const { return static_cast<int>(value); }
private:
    T value;
};

int main() {
MyClass<double> obj(3.14);
    int i = obj;
    std::cout << "i = " << i << std::endl;
    return 0;
}

In this example, we define a class template called MyClass that has a single template parameter T. The class has a conversion operator that converts an object of type MyClass<T> to an int by casting its value member to an int. We then create an object of type MyClass<double> with a value of 3.14, and use the conversion operator to convert it to an int in the main function.

Function Template Problem:

One problem that can occur with function templates is that the compiler may not be able to deduce the correct template parameter types from the function arguments. This can result in a compilation error, as the compiler is unable to instantiate the function template with the correct types.

Example:

#include <iostream>

template <typename T>
void print(T value) {
    std::cout << value << std::endl;
}

int main() {
    print(42);
    return 0;
}

In this example, we define a function template called print that takes a single parameter of type T and prints it to the console. We then call the print function with an int value in the main function. However, the compiler is unable to deduce the template parameter type from the argument, as there is no explicit type specified for the argument. This results in a compilation error.

To fix this problem, we can either specify the template parameter type explicitly (print<int>(42)), or provide additional overload functions that accept specific types (void print(int value)).

Generic Programming:

Generic programming is a programming paradigm that emphasizes the use of generic algorithms and data structures that can be used with a variety of types and values. It is based on the idea of abstraction and code reuse, and is often used in libraries and frameworks that are designed to be used in a wide range of applications.

Example:

#include <iostream>
#include <algorithm>
#include <vector>

template <typename T>
void print(const std::vector<T>& vec) {
    std::cout << "[ ";
    for (const auto& item : vec) {
        std::cout << item << " ";
    }
    std::cout << "]" << std::endl;
}

int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<double> vec2 = {2.71, 3.14, 4.13};
    std::sort(vec1.begin(), vec1.end());
    std::sort(vec2.begin(), vec2.end());
    print(vec1);
    print(vec2);
    return 0;
}

In this example, we define a function template called print that takes a std::vector of any type and prints its contents to the console. We then use this function template to print the contents of two different vectors of int and double values in the main function, and sort them using the std::sort algorithm from the standard library.

General Purpose Classes:

General purpose classes are classes that can be used for a variety of purposes, rather than being specific to a particular task or data type. They are typically designed to be reusable and flexible, and can be used in a wide range of applications.

Example:

template <typename T>
class Stack {
public:
    Stack() : top(0), max_size(100) { data = new T[max_size]; }
    ~Stack() { delete[] data; }
    void push(const T& item) { data[top++] = item; }
    T pop() { return data[--top]; }
    bool empty() const { return top == 0; }
private:
    T* data;
    size_t top;
    size_t max_size;
};

In this example, we define a class template called Stack that represents a stack of elements of type T. The template has a single template parameter T, which specifies the type of the elements in the stack. The Stack class provides several member functions for manipulating the stack, including push, pop, and empty.

Class Templates:

Class templates are used to create a family of classes that share a common structure or behavior but operate on different types or values. The class template itself is not a class, but a blueprint for creating classes. The actual class is created by instantiating the template with specific types or values.

Example:

template <typename T>
class Array {
public:
    Array(size_t size) : data(new T[size]), size(size) {}
    ~Array() { delete[] data; }
    T& operator[](size_t index) { return data[index]; }
    const T& operator[](size_t index) const { return data[index]; }
private:
    T* data;
    size_t size;
};

In this example, we define a class template called Array that represents a fixed-size array of elements of type T. The template has a single template parameter T, which specifies the type of the elements in the array. The Array class provides several member functions for accessing and manipulating the elements of the array.

Array Class Implementation (array.h):

The Array class implementation is a concrete example of a class template instantiation. It is created by instantiating the Array class template with a specific type or value.

Example:

#include "array.h"

int main() {
    Array<int> arr(5);
    for (size_t i = 0; i < arr.size(); i++) {
        arr[i] = i;
    }
    for (size_t i = 0; i < arr.size(); i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}

In this example, we instantiate the Array class template with the int type to create an array of integers with a size of 5. We then use a loop to set the values of the array to their indices, and another loop to print the values to the console.

Using the Array Template:

To use the Array class template, we must first include the template definition in our code. We can then instantiate the template with a specific type or value to create a concrete class.

Example:

#include "array.h"

int main() {
    Array<int> arr(5);
    for (size_t i = 0; i < arr.size(); i++) {
        arr[i] = i;
    }
    for (size_t i = 0; i < arr.size(); i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}

In this example, we include the array.h header file, which defines the Array class template. We then instantiate the template with the int type to create an array of integers with a size of 5. We use a loop to set the values of the array to their indices, and another loop to print the values to the console.

Template Parameters:

Template parameters are used to define the types or values that a class or function template operates on. They are specified in a template parameter list within angle brackets (<>), and can be of several different types, including type parameters, non-type parameters, and template parameters.

Example:

template <typename T, int N>
class Array {
public:
    Array() : data(new T[N]) {}
    ~Array() { delete[] data; }
private:
    T* data;
};

In this example, we define a class template called Array that represents a fixed-size array of elements of type T. The template has two template parameters: T, which is a type parameter that specifies the type of the elements in the array, and N, which is a non-type parameter that specifies the size of the array. The Array class uses these template parameters to allocate and deallocate the memory for the array.

Class Template Instantiation:

Class template instantiation is the process of creating a concrete class from a class template by replacing the template parameters with specific types or values.

Example:

template <typename T>
class MyClass {
public:
    void print() { std::cout << "Type: " << typeid(T).name() << std::endl; }
};

int main() {
    MyClass<int> obj1;
    MyClass<double> obj2;
    obj1.print();
    obj2.print();
    return 0;
}

In this example, we define a class template called MyClass that has a single template parameter T. The class has a member function called print that prints the type of the template parameter to the console using the typeid operator. We then instantiate the MyClass template with the int and double types to create two concrete classes, and call the print function on each object.

Non-Type ParameterConversion:

Non-type template parameters are values that are passed to a template as a parameter, but are not of a type. They can be of several different types, including integral types, enumerations, pointers, and references. Non-type parameters can be converted to other types using type conversion operators.

Example:

template <int N>
class MyClass {
public:
    void print() { std::cout << "Value: " << N << std::endl; }
};

int main() {
    MyClass<5> obj1;
    MyClass<3 + 2> obj2;
    obj1.print();
    obj2.print();
    return 0;
}

In this example, we define a class template called MyClass that has a single non-type template parameter N. The class has a member function called print that prints the value of the non-type parameter to the console. We then instantiate the MyClass template with the values 5 and 3 + 2 to create two concrete classes, and call the print function on each object.

Standard Template Library (STL):

The Standard Template Library (STL) is a collection of classes and functions that are part of the C++ standard library. It provides a set of generic programming tools that can be used to solve a wide range of programming problems. The STL is based on the principles of generic programming, which emphasizes the use of templates and algorithms that can operate on a wide range of data types and structures.

Example:

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> v{ 1, 2, 3, 4, 5 };
    std::reverse(v.begin(), v.end());
    for (auto x : v) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    return 0;
}

In this example, we use several components of the STL to reverse the elements of a vector of integers and print them to the console. We include the <vector> and <algorithm> headers, which provide the necessary classes and functions. We create a vector v with the values 1, 2, 3, 4, 5, and use the std::reverse algorithm to reverse the order of the elements in the vector. We then use a range-based for loop to print the elements of the vector to the console.

STL Components:

The STL is composed of several different components, including containers, iterators, algorithms, and function objects.

  • Containers: Containers are objects that store collections of elements. Examples include vectors, lists, maps, and sets.

  • Iterators: Iterators are objects that provide a way to access the elements of a container. They can be used to traverse the elements of a container and perform operations on them.

  • Algorithms: Algorithms are functions that operate on containers and their elements. They can be used to perform a wide range of operations, including sorting, searching, and transforming.

  • Function objects: Function objects are objects that behave like functions. They can be used as arguments to algorithms to customize their behavior.

Generic Programming:

Generic programming is a programming paradigm that emphasizes the use of templates and algorithms that can operate on a wide range of data types and structures. It is based on the idea of writing code that is independent of specific data types, but can be instantiated with specific types at compile time. This allows for the creation of reusable code that can be applied to a wide range of programming problems.

Example:

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 3, y = 5;
    double a = 1.5, b = 2.5;
    std::cout << max(x, y) << std::endl;
    std::cout << max(a, b) << std::endl;
    return 0;
}

In this example, we define a function template called max that returns the maximum of two values of the same type. The function takes two template parameters T and U, which represent the types of the values. We use the ternary operator ? : to return the larger of the two values. We then instantiate the max template with the types int and double to create two concrete functions, and call them with different values.

Last updated