[C++ Introduction] Template Elementary (Generic Programming)

1. Generic programming

Let's think about a small question: how to implement a general exchange function?

Before solving this problem, we hope to exchange two variables, these two variables are integer, or floating point, or other built-in types, and then their exchange can be completed with a function.
It is definitely impossible to solve this problem in C language, but in C++ it supports function overloading:

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

Although it is possible to use function overloading, there are several 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, one error may cause all overloads to be error

Can you tell the compiler a model and let the compiler use the model to generate code according to different types?
insert image description here

C++ introduces generic programming to solve this problem.

Generic programming:Writing generic code that has nothing to do with types is a means of code reuse.
Templates are the basis of generic programming, and are divided into function templates and class templates

insert image description here

With the help of templates, we can solve the above problems.

2. Function template

2.1 The concept of function template

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

2.2 Use of function templates

Function template format:

template<typename T1, typename T2,...,typename Tn>
return value type function name (parameter list) { }
note:typename is used to define template parameter keywords, and class can also be used (remember: struct cannot be used instead of class)

Let's go back to the problem above. With templates, we can solve it like this:

template<typename T>
void Swap(T& left, T& right)
{
    
    
	T temp = left;
	left = right;
	right = temp;
}

Here T is the type name of the template we defined. When we call Swap, what type of parameter is passed, T will be replaced with the corresponding type, and then the Swap function will process the parameter of this type accordingly

So the above problem code can be written like this:

Here is the quote

So here's a little question: Are our Swap(a, b) and Swap(c, d) above calling the same function?

The answer is no. Here we take a look through disassembly:
insert image description here
The addresses of the two functions here are different, not a function

2.3 The principle of function template

Everyone knows that when Watt improved the steam engine, mankind started the industrial revolution and liberated productivity. Machine production
eliminated many manual products. What is the essence, the repetitive work is handed over to the machine to complete. Someone gave an argument: lazy people create the world.
insert image description here

A function template is a blueprint, which is not a function itself, but a mold used by the compiler to generate a specific type of function
So in fact, the template is to hand over the repetitive things that we should do to the compiler.

So how does the compiler do it?

During the compiler compilation phase, for the use of template functions,The compiler needs to deduce and generate a function of the corresponding type according to the type of the actual parameter passed in for calling=.
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 specifically handles the double type, and the same is true for other types.
insert image description here

2.4 Instantiation of function templates

When using a function template with different types of parameters, the function template generates a specific function of the corresponding type parameter, which is called the 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 based on the actual parameter

Let's look at an example:

template<class T>
T Add(const T& left, const T& right)
{
    
    
	return left + right;
}
int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;

	cout << Add(a1, a2) << endl;
    cout << Add(d1, d2) << endl;
	return 0;
}


Let's take a look at the running results first:
insert image description here

Then what about us?

Here is the quote

The reason why the program reports an error here is:
At this time, there will be ambiguity when the function template is deduced and instantiated:
This statement cannot be compiled, because during compilation, when the function template is instantiated, its parameter type needs to be deduced. At this time, T is deduced as int by actual parameter a1, and T is deduced as double type by actual parameter d1, but there is only one T in the template parameter list, and the compiler cannot determine whether T should be determined as int or double type here and reports an error.
Note: In templates, the compiler generally does not perform type conversion operations.

In the face of this problem, we first propose a solution:

We can do the type conversion ourselves:
insert image description here

  1. Explicit instantiation: Specify the actual type of the template parameter in <> after the function name

We can also solve the above problem by this method:

Here is the quote

2.5 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
// 专门处理int的加法函数
int Add(int left, int right)
{
    
    
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
    
    
	return left + right;
}
int main()
{
    
    
    int a = 1, b = 2;
    Add(a,b);
    return 0;
}


Which one will this program call? Let's debug and see:
insert image description here
we can see that it is the first one through the debug mode.
Why is the first one called, because the compiler will also see which one is cheaper to call in this place. The first one can be called directly, but the second one needs to be instantiated with a template before it can be called.
So here the compiler picks the first one.

Of course, here we can call the generated template function by displaying instantiation:
insert image description here

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

3. Class template

3.1 Definition format of class template

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

Let's look at an example:

If we wanted to write a stack before, we would write it like this:

typedef int DataType;
class Stack
{
    
    
public:
	//构造函数
	Stack(size_t capacity = 4)
	{
    
    
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
private:
	DataType* _array;
	int _capacity;
	int _size;
};


write like thisIf we define two or more stacks in the main function and want them to store different types of data, is it difficult to do it? Because the data in the above code is of type, if we want the data to be of other types, do we have to do it again int? write a stack

But after learning templates, we can solve it like this:

3.2 Instantiation of class templates

template<class T>
class Stack
{
    
    
public:
	Stack(int capaicty = 4)
	{
    
    
		_a = new T[capaicty];
		_top = 0;
		_capacity = capaicty;
	}

	~Stack()
	{
    
    
		delete[] _a;
		_capacity = _top = 0;
	}

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


To be careful of:
1. Class template instantiation is somewhat different from function template instantiation. Class template instantiation can only be instantiated explicitly, that is, you need to follow the class template name with <>, and then put the instantiated type in <>.
2. The class template is not a real class, the result of its instantiation is the real class.

The function template instantiation can deduce the type of the template parameter according to the parameter type, but we use a class to create an object, such as the current stack, and will not directly pass the data type, so it must be explicitly instantiated: Note
insert image description here
:Here Stack is the class name Stack<int>and the type
When a member function in a class template is defined outside the class, a template parameter list needs to be added

When we define a class, we may be used to separate the header file and the source file. There is no problem with ordinary classes like this.
However, class templates are not good. If the class template is like this, the link will be wrong. As for the reason, we will go to the template later. I will talk about it when I talk about it, so everyone should understand it first.

Guess you like

Origin blog.csdn.net/weixin_69423932/article/details/132555699