Template of C + + foundation

Generic Programming

When learning 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 some disadvantages:

  • Overloaded functions only have different types and low code reuse rate. When new types appear, corresponding functions need to be added
  • The maintainability of the code is poor. An error may require modifying all overloaded functions

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

Generic programming refers to writing general code independent of type, which 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

Concept of function template

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.

Format of function template

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

// Take the Swap function described above as an example
template <class T>			 //Template parameter list - parameter type
void Swap(T& left, T& right) //Function parameter list - parameter object
{
	T tmp = left;
	left = right;
	right = tmp;
}

Note: the typename keyword can replace class to define template parameters, but struct cannot replace class.

Principle of function template

The function template is not a function, but a mold used by the compiler to generate specific types of functions, so the template gives the compiler the complex operations that should have been performed by us.

Consider a small question. Are the three 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 types, as shown in the following figure

At the same time, we found that the addresses of Call of different Swap functions are different through assembly, which can also verify that the results are correct!

Compiler compilation stage: the compiler deduces and generates the corresponding template function through the passed in argument type.

Instantiation of function template

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

  1. Implicit instantiation: let the compiler deduce the actual type of template parameters according to the actual parameters
template <class T>			 		 //Template parameter list - parameter type
T Add(const T& left, const T& right) //Function parameter list - parameter object
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.10, d2 = 20.20;
	// implicit instantiation 
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
	return 0;
}

Note: cout < < add (A1, D2) < < endl; Is wrong, because the two argument types are different, the compiler cannot deduce whether T is int or double.
There are two processing methods:
(1) User own cast

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

(2) Use display instantiation

  1. Display instantiation: specify the actual type of 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;

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
// Non template function
int Add(int left, int right)
{
	return left + right;
}
// Function template
template<class T>
T Add(T left, T right)
{
	return left + right;
}
void Test()
{
	Add(1, 2); 		// The compiler does not need specialization to match non template functions
	Add<int>(1, 2); // Call the Add version of the compiler specialization
}

Add(1, 2); It will directly match the non template function, because the compiler detects that there is an add function that specifically deals with int type, so it will not specialize with the function template;
Add<int>(1, 2); Is to display instantiation. The compiler will specialize an instance add function according to the function template.

  1. For non template functions and function templates with the same name, if other conditions are the same, non template functions will be called first; If the template can produce a function with a better match, the template will be selected
// Non template function
int Add(int left, int right)
{
	return left + right;
}
// Function template
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	return left + right;
}
void Test()
{
	Add(1, 2); 	 // It exactly matches the non function template type and does not require function template instantiation
	Add(1, 2.0); // The template function can generate a more matching version, and the compiler generates a more matching Add function according to the arguments
 number }

Add(1, 2); It completely matches the non template function and does not need template instantiation;
Add(1, 2.0); Although implicit type conversion is possible, a more matching Add(int, double) function can be specialized with the function template here.

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

Class template

Format of class template

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

Take the stack as the object, write a stack class template, and 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];
	}
	// Declared in class, defined outside class
	~Stack();
	void Push(const T& x);
private:
	T* _a;
	int _top;
	int _capacity;
};
// Destructors are defined outside the class
template <class T>
Stack<T>::~Stack()
{
	delete[] _a;
	_a = nullptr;
	_top = _capacity = 0;
}
// The insert function is defined outside the class
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 be followed by < >, and then put the instantiated type in < >. The class template name is not a real class, but the instantiation result is a real class.

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

Note: when learning a class, the class name and type are the same. However, in the class template, take stack as an example. Stack is the class name, stack < int >, stack < double >, stack < char > and stack < int * > are types

Keywords: C++ Back-end

Added by 6pandn21 on Mon, 21 Feb 2022 07:29:58 +0200