C++: Template advanced

Preface

In C++: Template Basics , the basic usage of templates has been introduced. Next, we will focus on issues such as template specialization.

1. Non-type template parameters

Template parameters are divided into: class type formal parameters and non-type formal parameters.
Class type parameters are: appear in the template parameter list, followed by the parameter type name such as class or typename.
A non-type parameter is to use a constant as a parameter of a class (function) template. The parameter can be used as a constant in the class (function) template.

In C++, we sometimes need to define a static array, static vector, static stack, etc. Let's take the static stack as an example. We want to see if this works?

#define N 10
template <class T>
class stack
{
    
    
private:
	T _a[N];
};

int main()
{
    
    
	stack<int> st1;
	stack<int> st2;
	return 0;
}

It is obviously established. But if we actually need st1 size to be 10 and st2 size to be 10000. If it is just the above simple macro replacement, it will cause a lot of waste of space. To this end, C++ introduced non-type template parameters. That is, in the template parameters, use aconstantAs a parameter of the class (function) template. We can determine the variable space size based on the size of the constant.

template <class T, size_t N>
class stack
{
    
    
private:
	T _a[N];
};

int main()
{
    
    
	stack<int, 10> st1;
	stack<int, 10000> st2;
	return 0;
}

Small tips:

  1. Floating point numbers, class objects, and strings are not allowed as non-type template parameters.
  2. Non-type template parameters must be confirmed at compile time. (That is, non-type template parameters cannot be passed to variables, because the compiler cannot determine how much space to open during compilation)

2. Specialization of templates

2.1 Concept

Normally, using templates can implement some type-independent code, but for some special types, you may get some wrong results and require special processing. For example, implementing a function template specifically for less than comparison

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{
    
    }

	bool operator<(const Date& d) const
	{
    
    
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d) const
	{
    
    
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
    
    
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
///
//上述为date类实现,下面是具体需求
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    
    
	return left < right;
}

int main()
{
    
    
	cout << Less(1, 2) << endl; // 可以比较,结果正确
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比较,结果错误
	return 0;
}

It can be seen that Less can be compared normally in most cases, but it will get wrong results in special scenarios. In the above example, d1 pointed
to by p1 is obviously smaller than the d2 object pointed by p2, but Less does not compare the contents of the objects pointed to by p1 and p2 internally, but compares the addresses of the p1 and p2
pointers , which cannot meet the expectations and is wrong. .
At this point, the template needs to be specialized. That is: a specialized implementation method for special types based on the original template class. Template specialization
is divided into function template specialization and class template specialization

2.2 Class template specialization

C++ class template specialization is divided into: full specialization and partial specialization

2.2.1 All specializations

Full specialization means that all parameters in the template parameter list are determined.
[Example]:

template<class T1, class T2>
class Data
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1, T2>" << endl; 
	}
private:
	T1 _d1;
	T2 _d2;
};

//全特化
template<>
class Data<int, double>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<int, double>" << endl;
	}
};
int main()
{
    
    
	Data<int, double> d2;//调用全特化模板
	return 0;
}

result:

Insert image description here

2.2 Partial specialization

Partial specialization refers to any specialized version that further restricts the design of template parameters.
Partial specialization has the following two expressions:

  1. Partial specialization: Specialize part of the parameters in the template parameter class table.

[Example]:

template<class T1, class T2>
class Data
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1, T2>" << endl; 
	}
private:
	T1 _d1;
	T2 _d2;
};

//偏特化
template<class T>
class Data<double,T>//将第一个参数特化为double
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T, double>" << endl;
	}
};
  1. Further restrictions on parameters: Partial specialization does not just mean specializing some parameters, but a specialized version designed to further restrict the template parameters.

[Example]:

template<class T1, class T2>
class Data
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1, T2>" << endl; 
	}
private:
	T1 _d1;
	T2 _d2;
};

//偏特化:两个参数偏特化为指针类型
template<class T1,class T2>
class Data<T1*, T2*>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1*, T2*>" << endl;
	}
};

//偏特化:两个参数偏特化为引用类型
template<class T1, class T2>
class Data<T1&, T2&>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1&, T2&>" << endl;
	}
};

int main()
{
    
    
	Data<int, int> d1;	//调用基础的模板
	Data<int*, int*> dd1;	//调用特化的指针版本
	Data<int&, int&> dd2;	//调用特化的指针版本
	return 0;
}

【operation result】:
Insert image description here

2.3 Function template specialization

Specialization steps for function templates:

  1. You must first have a basic function template
  2. The keyword template is followed by a pair of empty angle brackets <>
  3. The function name is followed by a pair of angle brackets, which specify the type to be specialized.
  4. Function parameter list: It must be exactly the same as the basic parameter type of the template function. If it is different, the compiler may report some strange errors.

[Example]:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    
    
	return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
    
    
	return *left < *right;
}
int main()
{
    
    
	cout << Less(1, 2) << endl;
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl;
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
	return 0;
}

Note: Generally, if a function template encounters a type that cannot be processed or is processed incorrectly, the function is usually given directly for simplicity of implementation
.

For example: in the actual writing process of the above Less function, in order to prevent the cost of copying parameters, we generally add references; in order to prevent the data from being modified, we generally use const modification. details as follows:

template<class T>
bool Less(const T& left, const T& right)
{
    
    
	return left < right;
}

//template<>
//bool Less<Date*>(Date* const & left, Date* const & right)//很怪,函数模板建议不要用特化
//{
    
    
//	return *left < *right;
//}

//我们发现此时函数模板特化很别扭,而且很容易写错。所以,对于函数模板一般不建议写特化,而是直接显示定义
template<class T>
bool Less(T* left, T* right)
{
    
    
	return *left < *right;
}

4. Template separation compilation

4.1 What is separate compilation?

A program (project) is implemented by several source files, and each source file is compiled separately to generate an object file. Finally,
the process of linking all the object files to form a single executable file is called separate compilation mode.

4.2 Separate compilation of templates

// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
``template<class T>
T Add(const T& left, const T& right)
{
    
    
 return left + right;
}
// main.cpp
#include"a.h"
int main()
{
    
    
 Add(1, 2);
 Add(1.0, 2.0);
 
 return 0;
}

【analyze】:
Insert image description here

4.3 Solutions

  1. It is actually possible to put the declarations and definitions in a file "xxx.hpp" or xxx.h. It is recommended to use this.
  2. Templates are explicitly instantiated where they are defined. This method is not practical and is not recommended.

5. Summary

【advantage】

  1. Templates reuse code, save resources, and enable faster iterative development. This is why the C++ Standard Template Library (STL) was born.
  2. Enhanced code flexibility

【defect】

  1. Templates can lead to code bloat and longer compilation times
  2. When a template compilation error occurs, the error message is very messy and it is difficult to locate the error.

Guess you like

Origin blog.csdn.net/Zhenyu_Coder/article/details/135300115
Recommended