[C++ Elementary] 6. Template Elementary (Function Template + Class Template)

generic programming

import - generic swap function

If you write a function for the exchange of two numbers. In C language, we would use the following method:

// 交换两个整型
void Swapi(int* p1, int* p2)
{
    
    
	int temp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
// 交换两个双精度浮点型
void Swapd(double* p1, double* p2)
{
    
    
	double temp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

Because the C language does not support function overloading, the function names of functions used to exchange different types of variables cannot be the same, and the form of parameter passing must be address passing, not value passing.
After learning the function overloading and reference of C++, we will use the following method to exchange two numbers:

// 交换两个整型
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;
}

C++'s function overloading allows functions used to exchange variables of different types to have the same function name, and parameters are passed by reference to make the code look less obscure.

However, this code still has its shortcomings:

  1. The multiple overloaded functions are only of different types, and the code reuse rate is relatively low. As long as a new type needs to be exchanged, a corresponding overloaded function needs to be added.
  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 automatically generate code according to different types ?

Just like playing with plasticine animals and other models when we were young, we can get animal models with the same shape but different colors by adding materials of different colors.
insert image description here

If such a mold can also exist in C++, by filling the mold with materials (types) of different colors, moon cakes with the same shape but different colors can be obtained (generate codes of specific types), that will greatly reduce code redundancy. Remain. Coincidentally, the predecessors have already planted the tree, and we only need to enjoy the shade here.

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

insert image description here

function template

The concept of function templates

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

format of function template

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
    
    
	//函数体
}

For example:

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

Notice:

  1. templateis a keyword defining a template, followed by angle brackets < >
  2. typenameIt is used to define template parameter keywords , and can also be used class(remember: struct cannot be used instead of class)
  3. T1, T2, ..., Tn represent the function name (just like the parameter name of the function), which can be understood as the name of the template, and you can choose the name yourself.

With function templates, we can solve our above exchange problem.
insert image description here
We can find that these different types, we have completed the exchange.

The principle of function templates

A function template is a blueprint, not a function itself. is the compiler's mold for generating functions of a particular concrete type. So in fact, the template is to hand over the repetitive things that we should have done to the compiler.
insert image description here
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 specifically handles the double type, and the same is true for the character 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 .

The above only reflects the exchange of variables of the same type. If we want to exchange variables of different types, an error will be reported when using the above function template, and our needs cannot be fulfilled. So what should we do?

1. Implicit instantiation: let the compiler deduce the actual type of the template parameter according to 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.1, d2 = 20.2;
	// 自动推演实例化
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;

	return 0;
}

This is still adding variables of the same type, so what if it is adding different types? Why can't the above template be used for adding variables of different types?

Add(a1, d1);//什么不行呢?

Note: In templates , the compiler generally does not perform type conversion operations , because once there is a problem with the conversion, the compiler needs to take the blame.

Because during compilation, when the compiler sees the instantiation, it needs to deduce its parameter type. T is deduced as an int by the actual parameter a1, and T is deduced as a double type by the actual parameter d1, but there is only one T in the template parameter list. The compiler cannot determine whether T should be determined as an int or a double type and reports an error.

At this point, we have three ways to deal with it. The first way is to convert b to int type when passing parameters . The second way is to use the display instantiation mentioned below . The third way is to use multiple parameter templates

Handling when different types of formal parameters are passed:

  1. Forced conversion when passing parameters (the corresponding formal parameters need const modification)
template<class T>
T Add(const T& left, const T& right)//const接收常性实参
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

	//对于只有一个参数模板,只能在传参时,自己去强转成一样的类型
	cout << Add((double)a1, d2) << endl;//强转,临时变量传参,具有常性
	cout << Add(a1, (int)d2) << endl;//强转,临时变量传参,具有常性

	return 0;
}

Use mandatory type conversion to convert the formal parameters to the same type during deduction.

  1. Explicit instantiation (implicit type conversion when passing parameters, corresponding formal parameters need const modification)
template<class T>
T Add(const T& left, const T& right)//需要使用const接收
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

	// 显示实例化
	//指定模板参数的实际类型为double
	cout << Add<double>(a1, d2) << endl;//显式实例化,a1发生隐式类型转换
	//指定模板参数的实际类型为int
	cout << Add<int>(a1, d2) << endl;//显式实例化,d2发生隐式类型转换

	return 0;
}
  1. Use multiple templates
template<class T1, class T2>//两个参数模板,可以传不同类型的参数  //可以写typename也可以写class
T1 Add(const T1& left, const T2& right)//返回值类型只能选择其中一种
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

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

	return 0;
}

Matching Principles for 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;
}
void Test()
{
    
    
	Add(1, 2); // 与非模板函数匹配,调用非模板函数,编译器不需要实例化
	Add<int>(1, 2); // 调用编译器实例化的Add版本
}
  1. 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 first when calling, and an instance will not be generated from the template. If the template can produce a function with a better match, then choose the template
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{
    
    
	return x + y;
}
//通用类型加法的函数模板
template<typename T1, typename T2>
T1 Add(const T1& x, const T2& y)
{
    
    
	return x + y;
}
int main()
{
    
    
	Add(10, 20); //与非模板函数完全匹配,不需要函数模板实例化
	Add(2.2, 2); //函数模板可以生成更加匹配的版本,编译器会根据实参生成更加匹配的Add函数
	return 0;
}
  1. Template functions do not allow automatic type conversion, but ordinary functions can do automatic type conversion
template<typename T>
T Add(const T& x, const T& y)
{
    
    
	return x + y;
}
int main()
{
    
    
	Add(2, 2.2); //模板函数不允许自动类型转换,不能通过编译
	//因为只有一个参数模板,只能在传参时,自己去强转成一样的类型
	return 0;
}

class template

The definition format of a class template

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

For example:

template<typename T>
class Scores
{
    
    
public:
	void Print()
	{
    
    
		cout << "数学:" << _Math << endl;
		cout << "语文:" << _Chinese << endl;
		cout << "英语:" << _English << endl;
	}
private:
	T _Math;
	T _Chinese;
	T _English;
};

Note: If a member function in a class template is defined outside the class, a template parameter list needs to be added.

template<class T>
class Scores
{
    
    
public:
	void Print();
private:
	T _Math;
	T _Chinese;
	T _English;
};
template<class T>
void Scores<T>::Print()
{
    
    
	cout << "数学:" << _Math << endl;
	cout << "语文:" << _Chinese << endl;
	cout << "英语:" << _English << endl;
}

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 <> (display instantiation). The class template name is not a real class , and the result of instantiation is the real class

The understanding of this sentence:

//Score不是真正的类,Score<int>和Score<double>才是真正的类
Score<int> s1;//需要显示实例化
Score<double> s2;

We also need to note that although they are instantiated from the same class template, the two instantiated classes are not the same class, because their types and sizes are different.
For example: the stack class we defined earlier

template<typename T>
class Stack
{
    
    
public:
	Stack(int capacity = 4)
	{
    
    
		cout << "Stack(int capacity = )" <<capacity<<endl;

		_a = (T*)malloc(sizeof(T)*capacity);
		if (_a == nullptr)
		{
    
    
			perror("malloc fail");
			exit(-1);
		}

		_top = 0;
		_capacity = capacity;
	}
	
	~Stack()
	{
    
    
		cout << "~Stack()" << endl;

		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)
	{
    
    
		// ....
		// 扩容
		_a[_top++] = x;
	}

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


int main()
{
    
    
	// 类模板一般没有推演时机,函数模板实参传递形参,推演模板参数
	// 类模板只能显示实例化
	// 他们是同一个类模板实例化出来的
	// 但是模板参数不同,他们就是不同类型
	Stack<double> st1; // double
	st1.Push(1.1);

	Stack<int> st2; // int
	st2.Push(1);

	return 0;
}

Guess you like

Origin blog.csdn.net/m0_58124165/article/details/127710923