Template programming of c + +

Template programming, container, multithreading

1, Template programming

1. Necessity of template programming

In c + +, the declaration of a variable must indicate its type, which improves the compilation efficiency, but in some cases, it has some defects. For example, you need to define a function to calculate the sum of two numbers. Since the value to be calculated in the future may be an integer or a floating-point number, you need to prepare corresponding functions for these types. However, the internal logic of these functions is the same, and the only difference between them is that they receive different data types. Then, is there a situation where the type feature is temporarily ignored during encoding, and is determined dynamically at runtime.

int add(inta ,int b){
    return a + b;
}

float add(float x , float y ){
    return x + y;
}

2. Function template

Function template is the description of general function. Use generics to define function, let compiler ignore type temporarily, pass type to template with parameters, and then let compiler generate function of corresponding type. Function template only represents a model. It is not a function that can be called directly. It needs to pass the corresponding type and generate the corresponding type of function when it is used.

The definition of a template starts with the template keyword, followed by a list of parameters. Use < > to include

template<typename T>
T add(const T& t1 ,const T& t2){
    return t1 + t2;
}


int result = add<int>( 3, 5);
cout <<"result = " << result << endl;


int result = add( 3, 5);
cout <<"result = " << result << endl;
  • Function template overload

Like ordinary functions, function templates can also be overloaded to meet more requirements flexibly

template<typename T>
T add(const T& t1 ,const T& t2){
    return t1 + t2;
}

template<typename T>
T add(const T& t1 , const T& t2 , const T& t3){
    return t1 + t2 + t3;
}

int result1 = add( 1, 2 );
int result2 = add( 1, 2 ,3);

cout <<"result1 = " << result1 << endl;
cout <<"result2 = " << result2 << endl;

  • Template variable parameters

If the number of parameters received by a function is uncertain, and all parameters are of the same type, the variable parameter method can be used to simplify the code. Variable parameter usage To represent, usually variable parameters are also called * * parameter package * *, which means that it can package multiple parameters into a whole.

3. Variable parameters

In C + +, the number of parameters of general functions is fixed, but there is also a special function whose number of parameters is variable. In view of this situation, C + + provides two methods of initializer list and ellipsis. The initializer list requires that the types of variable parameters must be consistent, while the ellipsis method does not have this limitation, so it is more flexible.

  • Ellipsis method

In order to deal with the variable parameters of ellipsis, the macro VA start, VA Arg, VA end and VA list of four macros need to be imported into ා include < stdarg. H >, and there is no way to calculate the number of variable parameters in this way, which needs to be specified in the front, and the number of variable parameters must be the same as the number of parameters actually passed, otherwise the result may be biased. The method of ellipsis does not limit the number of parameters or the type of parameters. It can be different data types.

//num indicates that there are several parameters,... Indicates the specific parameters on the right side
// Call: add (3, "aa","bb","cc");

int add(int count, ...) {
    //count indicates the number of variable parameters
    va_list vl;//Declare a VA list variable
    va_start(vl, count);//Initialization, the second parameter is the last determined parameter
    
    int sum = 0;
    for (int i = 0; i < count; i++)
        sum += va_arg(vl, int); //Read variable parameter, two parameters of which are of variable parameter type

    va_end(vl);    //Clean-up work
    return sum;
}
  • Initializer list mode

A method introduced by c++ 11 needs to import ා include < initializer ﹐ list >, parameters must be placed in a group of {}, and elements must be of the same type.

int add(initializer_list<int> il) {
    int sum = 0;
    for (auto ptr = il.begin(); ptr != il.end(); ptr++){ 
        sum += *ptr;
    }
    return sum;
}

int main(){
    add({10,20,30}); //The passed parameter must be placed in a {}
    return 0 ;
}

4. Variable parameter template

The function template solves the situation that the same function and different data types cause multiple method overloads. When the parameter types of the template are the same, or there are multiple same parameter types, it is more beautiful to use variable parameters to improve the template function. For parameter packages, besides getting the size, programmers want to pay more attention to how to get the data and deconstruct the elements. There is also a name: extension.

1. Define variable parameter template

//Defines an add function that accepts a type of variable argument.
// Args above: template parameter package, indicating that there are many types of parameters that can be passed. Here is the description type
// The following args1: when the function parameter package calls the function, it means that a lot of data can be passed in, matching the above type.

template <typename ...Args>
int add(Args...args1){
   int a =  sizeof...(args1);
   cout <<"The parameters passed in are:"<< a << endl;
}

//Args is: int, int, int, string, string, string
// args1 : 2, 3, 4, a, b, c
add(2,3,4,"a","b","c");

2. Expand parameter package

Variable parameters are convenient from the design point of view, but it's a bit tricky to get the parameters passed, because index subscripts are useless in the scenario of expanding parameter packages, and variable parameters can be regarded as a package, which contains several passed parameters. In general, the method of expanding parameter package is to use recursion, call continuously, without calling once, reduce one parameter, and finally capture all parameters.

  • Recursive call

This is a flawed code because it's trapped in infinite recursion. Although it is the wrong code, it provides a way to unpack the parameter package. The core idea is to unpack the parameter package, deal with the first item, and then pass the rest down. In this way, in each layer of the call, you can get a new first number.

int add(Args...args){
   add(args...) //args1... Indicates the parameter package, which is the package of all parameters passed.
}
  • Recursive call
//An empty function is defined here for the final recursive termination.
int add(){return 0 ;}
template <typename T, typename ...Args>
int add(T t , Args...args){

    //int sum = 0;
    cout <<"When printing numbers:" << t << endl;
    
    //At the beginning, the loop calls itself until the last one without parameters calls the external add()
    t += add(args...);
    return t;
}
int main(){
	cout << add(1,2,3,4,5) << endl;
}

5. Class template programming

Sometimes inheritance and inclusion can not meet the need of reusing code, which is particularly prominent in Container classes. For example, we have defined a Container class, Container. This Container class can achieve the same work as a vertor, can save data, can modify data, and there is no limit to the type of data, but the operation for data is the same. So class template programming is the best choice.

1. Define template class

Here, the stack is used as a reference object to define a template class to achieve the same functions as the stack.

  • Original code
class Stack{
private :
    enum{MAX = 10}; //Indicates that this Stack container can only hold 10 at most.
    int top =0 ; //Represents the index position at the top
    string items[MAX]; //Define an array to hold 10 elements at a time
public:
    bool isempty(){
        return top == 0;
    }
    bool isfull(){
        return top == MAX;
    }
    //Pressing stack
    int push(string val){
        if(isfull()){
            return -1;
        }
        //You can deposit it if it's not full
        items[top++] = val;
    }
    //Stack out
    string pop(){
        if (isempty()){
            return "";
        }
        //If it's not empty top, it just points to the location, and the array gets the data, the index starts from 0, so first--
        return items[--top] ;
    }

    string operator[](int index){
        if(isempty() || index > --top){
            cout <<"Container is empty or out of bounds" << endl;
            return "";
        }
        return items[index];
    };
};
  • Class template

The above Stack container can only be used for string data type. If you want to save custom types or other types, then Stack cannot be satisfied. To define a class template, you need to use template < typename T > in front of the class, and then replace all the strings in it, so that Stack can work for all types. If it's a custom type, you need a custom type to provide a parameterless constructor, because the definition of an array performs the construction of an object. If you want to avoid the construction work, you can use allocator to operate.

template <typename T> class Stack{
private :
    enum{MAX = 10}; //Indicates that this Stack container can only hold 10 at most.
    int top =0 ; //Represents the index position at the top
    T items[MAX]; //Define an array to hold 10 elements at a time
public:
    bool isempty(){
        return top == 0;
    }
    bool isfull(){
        return top == MAX;
    }
    //Pressing stack
    int push(const T& val){
        if(isfull()){
            return -1;
        }
        //You can deposit it if it's not full
        items[top++] = val;
    }
    //Stack out
    T pop(){
        if (isempty()){
            return "";
        }
        //If it's not empty top, it just points to the location, and the array gets the data, the index starts from 0, so first--
        return items[--top] ;
    }

    T operator[](int index){
        if(isempty() || index > --top){
            cout <<"Container is empty or out of bounds" << endl;
            return "";
        }
        return items[index];
    };
};

6. practice

Simulate vector to implement custom container

  1. The bottom layer is implemented by array
  2. Array opens up space and allocator is used, otherwise object construction will be performed in one block
  3. Can fit any type
  4. Provides the push back function to add elements to the end of the container
  5. Provide pop function to return the front-end element of the container
  6. Provide the [] operator to get the elements according to the subscript
  7. Provide size function
  8. Provides the * operator to modify the specified element. Such as:

Stu stu = myvector[0];

*stu.name = "Zhang San";

Published 31 original articles, praised 0, visited 709
Private letter follow

Tags: Programming encoding REST

Posted on Sun, 09 Feb 2020 22:58:22 -0800 by somenoise