Templates for C++ Basics

generic programming

When we learned about function overloading before, we wrote a swap function Swap

void Swap(int& left, int& right)
{
    
    
	int tmp = left;
	left = right;
	right = tmp;
}
void Swap(char& left, char& right)
{
    
    
	char tmp = left;
	left = right;
	right = tmp;
}
void Swap(double& left, double& right)
{
    
    
	double tmp = left;
	left = right;
	right = tmp;
}
int main()
{
    
    
	int a = 0, b = 1;
	char c = 'A', d = 'B';
	double e = 1.1, f = 2.2;
	Swap(a, b);
	Swap(c, d);
	Swap(e, f);
	
	return 0;
}

Although C++ supports function overloading, which allows us to exchange multiple types of variables at the same time, there are still downsides:

  • The overloaded functions are only of different types, and the code reuse rate is low. When a new type appears, the corresponding function needs to be added
  • The maintainability of the code is poor, and an error may require modifying all overloaded functions

In order to solve the above problems, C++ proposes the concept of generic programming ! ! !

Generic programming refers to writing generic code that has nothing to do with types, and is a general means of code reuse. Templates are the foundation of generic programming

Templates are divided into function templates and class templates

function template

The concept of function templates

A function template represents a family of functions that are type-independent, parameterized when used, and produce a type-specific version of the function based on the type of the arguments.

Format of the function template

template <class T1, class T2>
return value type function name (parameter list)
{ //… }

// 以上面介绍的Swap函数为例
template <class T>			 //模板参数列表  ——参数类型
void Swap(T& left, T& right) //函数参数列表  ——参数对象
{
    
    
	T tmp = left;
	left = right;
	right = tmp;
}

Note: The typename keyword can replace class for defining template parameters, but struct cannot replace class.

The principle of function template

A function template is not a function, it is a mold used by the compiler to generate a specific type of function, so the template gives the compiler the execution of complex operations that should be performed by us.

Think about a small question, are the 3 Swap functions in the main function the same?

template <class T>			
void Swap(T& left, T& right) 
{
    
    
	T tmp = left;
	left = right;
	right = tmp;
}
int main()
{
    
    
	int a = 0, b = 1;
	char c = 'A', d = 'B';
	double e = 1.1, f = 2.2;
	Swap(a, b);
	Swap(c, d);
	Swap(e, f);
	return 0;
}

The answer is obviously different, because the parameter types of each Swap function are different. The three Swap functions will be converted into different template functions according to the actual parameter parameter types, as shown in the following figure.
insert image description here
At the same time, we find the addresses of different Swap functions Call through assembly Different, you can also verify that the result is correct!
insert image description here
Compiler compilation phase: The compiler deduces and generates the corresponding template function by passing in the actual parameter type .

instantiation of function templates

When a function template is used with parameters of different types, it is called instantiation of the function template. Template parameter instantiation is divided into: implicit instantiation and explicit instantiation .

  1. Implicit instantiation: Let the compiler deduce the actual type of the template parameter from the actual parameter
template <class T>			 		 //模板参数列表  ——参数类型
T Add(const T& left, const T& right) //函数参数列表  ——参数对象
{
    
    
	return left + right;
}
int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.10, d2 = 20.20;
	// 隐式实例化
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
	return 0;
}

Note : cout << Add(a1, d2) << endl;It is not correct, because the two actual parameters are of different types, the compiler cannot deduce whether T is int or double.
There are two processing methods at this time:
(1) The user forces the conversion by himself

	cout << Add(a1, (int)d2) << endl;
	cout << Add((double)a1, d2) << endl;`

(2) Use display instantiation

  1. Show instantiation: specify the actual type of the template parameter in <> after the function name
	cout << Add<int>(a1, a2) << endl;
	cout << Add<double>(d1, d2) << endl;
	cout << Add<int>(a1, d2) << endl;
	cout << Add<double>(a1, d2) << endl;

The matching principle of template parameters

  1. A non-template function can exist at the same time as a function template with the same name, and the function template can also be instantiated as this non-template function
// 非模板函数
int Add(int left, int right)
{
    
    
	return left + right;
}
// 函数模板
template<class T>
T Add(T left, T right)
{
    
    
	return left + right;
}
void Test()
{
    
    
	Add(1, 2); 		// 与非模板函数匹配,编译器不需要特化
	Add<int>(1, 2); // 调用编译器特化的Add版本
}

Add(1, 2);It will directly match the non-template function, because the compiler detects that there is an Add function that specifically handles the int type, so it will not use the function template specialization;
Add<int>(1, 2);it is a display instantiation, and the compiler will specialize an instance Add function according to the function template.

  1. For non-template functions and function templates of the same name, if other conditions are equal, the non-template function will be called first; if the template can produce a function with a better match, the template will be selected
// 非模板函数
int Add(int left, int right)
{
    
    
	return left + right;
}
// 函数模板
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
    
    
	return left + right;
}
void Test()
{
    
    
	Add(1, 2); 	 // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函}

Add(1, 2);Exactly match with non-template functions, no template instantiation is required;
Add(1, 2.0);although implicit type conversion is possible, function templates can be used here to specialize more matching Add(int, double)functions.

  1. Template functions do not allow automatic type conversion, but normal functions can do automatic type conversion

class template

class template format

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

Taking the stack as the object, write a stack class template, the code is as follows:

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

PS : At this time, if you want to declare the member function of the class in the class and define it outside the class, you need to add a template parameter list

template <class T>
class Stack
{
    
    
public:
	Stack(int capacity = 4)
		:_top(0)
		, _capacity(capacity)
	{
    
    
		_a = new T[capacity];
	}
	// 在类中声明,类外定义
	~Stack();
	void Push(const T& x);
private:
	T* _a;
	int _top;
	int _capacity;
};
// 析构函数在类外定义
template <class T>
Stack<T>::~Stack()
{
    
    
	delete[] _a;
	_a = nullptr;
	_top = _capacity = 0;
}
// 插入函数在类外定义
template <class T>
void Stack<T>::Push(const T& x)
{
    
    
	//...
}

instantiation of class template

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 instantiated The result is the real class.

int main()
{
    
    
	Stack<int> s1;
	Stack<double> s2;
	Stack<char> s3;
	Stack<int*> s4;
	return 0;
}

Note: When learning about classes earlier, the class name and type were the same . But in the class template, take the stack as an example, Stackas the class name , Stack<int>, Stack<double>, Stack<char>and Stack<int*>as the type

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324135498&siteId=291194637