C + + template (initial level)

Posted by drepster on Sun, 06 Mar 2022 11:06:34 +0100

When learning C language, we all wrote a function called swap, which is used to exchange the values of two variables. Like this.

void Swap(int& left, int& right){
    int temp = left;
    left = right;
    right = temp;
}

void Swap(double& num1, double& num2){
    int tmp = num1;
    num1 = num2;
    num2 = tmp;
}
...

We found that if we want to exchange two variables of type int, we need a function whose parameter is two ints. If we want to exchange two variables of type double, we need to write a Swap function to implement two variables of type double. This will be very troublesome, so c + + has the concept of a template.

Function template

Concept: the function template represents a function family. The function template is independent of type and is parameterized when used. The specific type version of the function is generated according to the argument type.

Function template format:

template<typename T1>

template<typename T1, typename T2 ... typename Tn>

template<class T1>

template<class T1, class T2, ... class Tn

Return value type function name (parameter list) {}

All of the above are OK

template<typename T>
void swap(T& num1, T& num2){
    T tmp = num1;
    num1 = num2;
    num2 = tmp;
}

int main(){    
    int i1 = 10;
    int i2 = 20;
    Swap(i1, i2);
    
    double d1 = 2.0;
    double d2 = 5.0;
    Swap(d1, d2);
    return 0;
}

ps: typename is a keyword used to define template parameters. You can also use class

Principle of function template

Function template is a blueprint. It is not a function itself. It is a mold that the compiler uses to generate specific types of functions. So in fact, the template is to give the compiler the repeated things we should have done.

In the compiler compilation stage, for the use of function templates, the compiler needs to deduce and generate functions of corresponding types according to the input argument types for calling. For example, when using the function template with the double type, the compiler determines T as the double type through the deduction of the argument type, and then generates a code specially dealing with the double type

Instantiation of function template

Using function templates with different types of parameters to automatically generate corresponding functions is called instantiation of function templates. Function template instantiation is divided into implicit instantiation and display instantiation

Implicit instantiation: let the compiler deduce the actual type of template parameters according to the actual parameters

#include<iostream>
using namespace std;
template<class T>
T Add(const T& num1, const T& num2){
    return num1 + num2;
}

int main(){
    int a1 = 10, a2 = 20;
    double d1 = 2.1, d2 = 3.1;
    cout << Add(a1, a2) << endl;
    cout << Add(d1, d2) << endl;
    
    Add(a1, d1); // Can this statement be compiled
    
    Add(a1, (int)d1); // Can this statement be compiled?
}

Display instantiation: specify the actual type of template parameter after the function name

int main(){
    int a =10;
    int d = 2.1;
    
    // Explicit Instantiation 
    Add<int>(a, b); //  Specifies that T is of type int
    return 0;
}

If the types do not match, the compiler will attempt implicit type conversion. If the conversion fails, the compiler will report an error

Matching principle of template parameters

  1. A non template function can exist simultaneously with a function template with the same name, and the function template can also be instantiated as the non template function

    // An addition function that deals specifically with int
    int Add(int num1, int num2){
        return num1 + nim2;
    }
    
    // General addition function
    template<class T>
    T Add(T num1, T num2){
        return num1 + num2;
    }
    
    int main(){
        
        Add(1, 2);  // The compiler does not need to instantiate to match non template functions
        Add<int>(1, 2); // Call the Add version instantiated by the compiler
        
        return 0;
    }
    
  2. For non template functions and function templates with the same name, if other conditions are the same, the non template function will be called first when calling, and an instance will not be generated from the template. If the template can produce a function with better matching, the template will be selected

// An addition function that deals specifically with int
int Add(int num1, int num2){
    return num1 + num2;
}

// General addition function
template<class T1, class T2>
T1 Add(T1 num1, T2 num1){
    return num1 + num2;
}

int main(){
    
    Add(1, 2);  // It exactly matches the non function template type and does not require function template instantiation
    Add(1, 3.2);  // Function templates can produce more matching versions
    
    return 0;
}

Class template

Definition format of class template

template<class T1, class T2, ... ,class Tn >
class Class template name
{
    // Class member definition
};

Before designing a dynamic sequence table, if we want to design an int sequence table and a char sequence table, we need to implement them separately. With a class template, we can write a template to let the compiler automatically identify the data type and generate the corresponding class.

// Dynamic sequence table
// Note: Vector is not a concrete class, but a mold for the compiler to generate a concrete class according to the instantiated type
template<class T>
class Vector
{
public:
    Vector(size_t capacity = 10)
        :_pData(new T[capacity])
        ,_size(0)
        ,_capacity(capacity)
    {}
    
    // Use the destructor demonstration to declare in the class and define outside the class
    ~Vector();
    void PushBack(const T& data);
    void PopBack();
    
    //...
    T& operator[](size_t pos)
    {
        assert(pos <_size);
        return _pData[pos];
    }
private:
    T* _pData;
    size_t _size;
    size_t _capacity;
};

// Note: when the functions in the class template are defined outside the class, the template parameter list needs to be added
template <class T>
Vector<T>::~Vector()
{
    if(_pData)
    {
        delete[] _pData;
    }
    _size = capacity = 0;
}

// Instantiation of class template
int main(){
    Vector<int> s1;
    Vector<char> s2;
    return 0;
}

Class template instantiation is different from function template instantiation. Class template instantiation needs to follow the class template name with < >, and then put the instantiated type in < >. The class template name is not a real class, but the instantiation result is a real class

Topics: C++ Back-end