[C++] Elementary templates - function templates and class templates

1. Generic programming

Let's first look at a swap function

void Swap(int& a,int& b)
{
    
    
	int c = a;
	a = b;
	b = c;
}

void Swap(double& a, double& b)
{
    
    
	double c = a;
	a = b;
	b = c;
}

void Swap(char& a, char& b)
{
    
    
	char c = a;
	a = b;
	b = c;
}

For a simple Swap function like this, because the types of parameters are different, we need to create multiple Swap functions for different types. Although this situation can be achieved by function overloading, there are two disadvantages:

  1. The overloaded functions are only of different types, and the code reuse rate is relatively low. As long as a new type appears, the user needs to add the corresponding function
  2. The maintainability of the code is relatively low. If one error occurs, all overloads may be errored.

Then can we tell the compiler a model, let the compiler use the model to generate code according to different types ?

The ancients seem to have faced such a problem. One person wrote an article that shines in the past and the present, and wanted to spread it, but limited by the development of technology, he could only copy it by hand, which limited the spread of knowledge. spread until the advent of movable type printing revolutionized the phenomenon

insert image description here

Using the frame as a template, as long as we replace the modules inside, we can print different articles. We have to admire the wisdom of the ancients.

If such a mold can also exist in C++ , it will save us a lot of time and delay the speed of hair loss. It just so happens that there is such a method in C++.

Generic programming: Writing generic code that has nothing to do with types is a means of code reuse. Templates are the foundation of generic programming.

In C++, templates are divided into function templates and class templates .

2. Function template

A function template represents a family of functions. The function template has nothing to do with the type. It is back-parameterized when used, and a specific type version of the function is generated according to the type of the actual parameter.

The function template format is as follows:

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

Return value type function template name(T1 parameter name, ..., Tn parameter name){}

Notice:

  1. typename is a keyword used to define template parameters, and class can also be used , but struct cannot be used.
  2. The template setting template parameters and the following functions are collectively called function templates . The corresponding template parameters cannot be used in other functions. If you want to use them, you can only continue to create function templates.
  3. If you set several template parameters, you need to use several. If there are no leftovers, the compiler will report an error, as follows:
template<class T,class T2>
T Add(const T& a, const T& b)
{
     
     
	return a + b;
}

int main()
{
     
     
    int a1 = 1,a2 = 2;
    Add(a1,a2);
    return 0;
}

insert image description here

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

int main()
{
    
    
	int a = 1;
	int b = 2;
	Swap(a, b);
	cout << a << " " << b << endl;

	return 0;
}

insert image description here

2.1 The principle of function template

A function template is a template, not a function, but a mold that the compiler uses to generate a function of a specific concrete type. So in fact, the template is to hand over the repetitive things that we should have done to the compiler.

We write the following code to check whether the address of the calling function template is the same (check it in the assembly environment, if you debug directly, all function templates are called):

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

int main()
{
    
    
	int a = 1, b = 2;
	Swap(a, b);

	double c = 1.0, d = 2.0;
	Swap(c, d);

	return 0;
}

insert image description here

As shown in the figure above, the addresses of the functions called twice are different.

And if it is the following two calls of the same type of function, the address is the same:

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

int main()
{
    
    
	int a = 1, b = 2;
	Swap(a, b);

	int c = 3, d = 4;
	Swap(c, d);

	return 0;
}

insert image description here

Conclusion: In the compiler compilation stage, for the use of template functions, the compiler needs to deduce and generate corresponding types of functions for calling according to the type of actual parameters passed in. For example: when using a function template with a double type, the compiler determines T as a double type through the deduction of the actual parameter type, and then generates a code that deals specifically with the double type. The same is true for the character type. The same type calls the same The function

insert image description here

expand:

The Swap function is a frequently used function, and it is defined in the library. If we want to use it, we can directly call the swap function in the library.

insert image description here

#include<iostream>
using namespace std;

int main()
{
    
    
	int a = 1, b = 2;
	swap(a, b);
	cout << a << " " << b << endl;

	return 0;
}

insert image description here

2.2 Instantiation of function templates

When a function template is used with parameters of different types , it is called instantiation of the function template . There are two types of template parameter instantiation: implicit instantiation and explicit instantiation.

  1. Implicit instantiation: Let the compiler deduce the actual type of the template parameter based on the actual parameter.

    template<class T>
    T Add(const T& a, const T& b)
    {
          
          
    	return a + b;
    }
    
    int main()
    {
          
          
    	int a1 = 1, a2 = 2;
    	cout << Add(a1, a2) << endl;
    
    	double b1 = 1.1, b2 = 2.2;
    	cout << Add(b1, b2) << endl;
    
    	return 0;
    }
    

    insert image description here

    If the following statement is executed, an error will be reported

    	Add(a1, b1);
    

    Because during compilation, when the compiler sees the instantiation, it needs to deduce its parameter type, deduce T as an int through the actual parameter a1, and deduce T as a double type through the actual parameter b1, but there is only one T in the template parameter list , the compiler cannot determine what type T is, so an error is reported.

    Note: The compiler will not perform automatic type conversion in the template, because the compiler is a compiler after all, it is not so intelligent, and it does not know the programmer's mind, knowing whether to convert to int or double, so it does not convert directly.

    There are three ways to solve this problem:

    1. User-mandated type conversion

      	Add(a1, (int)b1);//结果为int类型的值
      	Add((double)a1, b1);//结果为double类型的值
      
    2. When creating a template parameter, use two template parameters to receive two parameters

      template<class T1,class T2>
      T1 Add(const T1& a, const T2& b)
      {
              
              
      	return a + b;
      }
      
      int main()
      {
              
              
      	int a1 = 1;
      	double b1 = 1.1;
      
      	cout << Add(a1, b1) << endl;
      
      	return 0;
      }
      

      Defect: But what type needs to be returned still needs to be provoked by the programmer.

    3. Instantiate using display

      	Add<int>(a1,b1);//将参数全部置为int类型
      	Add<double>(a1,b1);//将参数全部置为double类型
      
  2. Explicit instantiation: Specify the actual type of the template parameter in <> after the function name.

    int main()
    {
          
          
    	int a1 = 1
    
    	double b1 = 1.1
    	
    	cout << Add<int>(a1, b1) << endl;
    	cout << Add<double>(a1, b1) << endl;
    	return 0;
    }
    

    insert image description here

    If the types do not match, the compiler will try to perform implicit type conversion according to the type in <>, and if the conversion fails, the compiler will report an error.

2.3 Matching principle of template parameters

  1. A non-template function can coexist with a function template with the same name, and the function template can also be instantiated as this non-template function

    template<class T>
    T Add(const T& a)
    {
          
          
    	return a + b;
    }
    
    int Add(const int& a, const int& b)
    {
          
          
    	return a + b;
    }
    
    int main()
    {
          
          
    	Add(1, 2);
    	Add<int>(1, 2);
    
    	return 0;
    }
    

    As mentioned above Add(1,2), the non-template function is called, because the passed parameter is an int type, and the non-template function can be used directly, while the template function needs to be instantiated, and the compiler will choose to call a more time-saving and labor-saving function.

    However Add<int>(1,2), because the existence of the explicit instantiation will only call the function template, instantiate it as the same function as the non-template function and then execute the function.

  2. For a non-template function and a function template with the same name, if other conditions are the same, the non-template function will be called preferentially when calling and will not produce an instance from the template. If the template can produce a function with a better match, then the template will be selected .

    template<class T1,class T2>
    T1 Add(const T1& a, const T2& b)
    {
          
          
    	return a + b;
    }
    
    int Add(const int& a, const int& b)
    {
          
          
    	return a + b;
    }
    
    int main()
    {
          
          
    	Add(1, 2);//与非模板函数匹配,编译器不需要特化
    	Add(1, 2.0);//调用函数模板
    
    	return 0;
    }
    

    Add(1,2)It is completely consistent with non-template functions, and calls non-template functions, saving time and effort.

    Add(1,2.0)The types are different, and functions of the same type can only be instantiated through templates and called templates.

  3. Template functions do not allow automatic type conversion, but ordinary functions can.

    int Add(const int& a, const int& b)
    {
          
          
    	return a + b;
    }
    
    int main()
    {
          
          
    	cout << Add(1, 2.0) << endl;
    
    	return 0;
    }
    

    insert image description here

3. Class Templates

3.1 Definition format of class template

template<class T1,class T2,...,class Tn>
class 类模板名
{
    
    
	//类内成员定义
};

Write the following code using a class template

template<class T>
class Stack
{
    
    
public:
	Stack(int capacity = 4)
	{
    
    
		_a = new T[capacity];
		_top = 0;
		_capacity = capacity;
	}
    //声明和定义分离
	~Stack()

private:
	T* _a;
	int _capacity;
	int _top;
};

//在类外定义
template<class T>
Stack<T>::~Stack(){
    
    
    delete[] _a;
    _top = _capacity = 0;
}

Notice:

  1. The functions in the class template are declared in the class, and the writing method of the definition outside the class should be paid attention to, which is different from the normal class.
  2. If the declaration and definition of a class template are separated, it needs to be completed in one file and cannot be split into two files. For example, it is not allowed to put the declaration in .hthe file and the definition in the file..c

3.2 Instantiation of class templates

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 the real class instantiation structure is the real class.

Using the class template written above

template<class T>
class Stack
{
    
    
public:
	Stack(int capacity = 4)
	{
    
    
		_a = new T[capacity];
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
    
    
		delete[] _a;
		_top = _capacity = 0;
	}
private:
	T* _a;
	int _capacity;
	int _top;
};

int main()
{
    
    
	Stack<int> st1;
	Stack<char> st2;

	return 0;
}
  • Stack is not a class name, not a real class, Stack<type> is a class

Guess you like

Origin blog.csdn.net/m0_52094687/article/details/129543359