C++: Templates advanced

 Table of contents

1. Non-type template parameters

2. Template specialization

2.1 Concept

2.2 Specialization of function templates

2.3 Specialization of class templates

2.3.1 All specializations

2.3.2 Partial specialization

3. Separate compilation of templates

3.1 What is separate compilation?

3.2 Separate compilation of templates

3.3 Solutions

4. Template summary


1. Non-type template parameters

  • Template parameter classification:Type parameter andNon-type parameter.
  • Type parameters are: appear in the template parameter list, followed by the parameter type name such as class or typename.
  • Non-type parameter: Use a constant as a parameter of the class (function) template. The parameter can be used as a constant in the class (function) template.
namespace bite
{
    // 定义一个模板类型的静态数组
    template<class T, size_t N = 10>
    class array
    {
    public:
        T& operator[](size_t index){return _array[index];}
        const T& operator[](size_t index)const{return _array[index];}
        size_t size()const{return _size;}
        bool empty()const{return 0 == _size;}
    private:
        T _array[N];
        size_t _size;
    };
}

Notice:

  • 1. Floating point numbers, class objects and strings are not allowed as non-type template parameters.
  • 2. The result of non-type template parameters must be confirmed at compile time.

2. Template specialization

2.1 Concept

Normally, using templates can implement some type-independent code, butfor some special types you may get some errors As a result, requires special processing, for example: implements a function template specifically used for less than comparison. When we When passing in a pointer, our purpose is not to compare the size of the pointer, but to compare the content pointed to by the pointer.

// 函数模板 -- 参数匹配
#include<iostream>
using namespace std;
class Date
{
public:
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    bool operator<(const Date& date)
    {
        if (_year < date._year)
        {
            return true;
        }
        else if (_year == date._year)
        {
            if (_month < date._month)
            {
                return true;
            }
            else if(_month == date._month)
            {
                if (_day < date._day)
                {
                    return true;
                }
            }
        }
        return false;
    }
private:
    int _year;
    int _month;
    int _day;
};

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 expectations and is an error.

At this time, you needto specialize the template. That is:A specialized implementation method based on the original template class for special types. Template specialization is divided into function template specialization and class template specialization

2.2 Specialization of function templates

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 <>, because the function template only has full specialization. Full specialization will be discussed below.
  3. The function name is followed by a pair of angle brackets that specify the type to be specialized.
  4. Function parameter list: must be exactly the same as the basic parameter type of the template function. If different compilers may report some strange errors
// 函数模板 -- 参数匹配
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.

bool Less(Date* left, Date* right)
{
    return *left < *right;
}

This kind of implementation is simple and clear, the code is highly readable and easy to write, because for some function templates with complex parameter types, specialization is given, so specialization of function templates is not recommended.

Example:const modifies the pointing of the pointer.

template<class T>
bool Less(const T& left, const T& right)
{
	return left < right;
}
//特化
template<>
bool Less<int*>(int* const & left, int* const & right)//const放在指针前会修饰指针指向的内容
{
	return *left < *right;
}

2.3 Specialization of class templates

2.3.1 All specializations

Full specialization means that all parameters in the template parameter list are determined.

template<class T1, class T2>
class Data
{
public:
    Data() {cout<<"Data<T1, T2>" <<endl;}
private:
    T1 _d1;
    T2 _d2;
};
//全特化
template<>
class Data<int, char>
{
public:
    Data() {cout<<"Data<int, char>" <<endl;}
private:
    int _d1;
    char _d2;
};
void TestVector()
{
    Data<int, int> d1;
    Data<int, char> d2;
}

2.3.2 Partial specialization

Partial specialization: Any specialization that further restricts the design of template parameters. For example, for the following template class:

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

Partial specialization has the following two expressions:

1. Partial specialization

Specialize some parameters in the template parameter class table.

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
    Data() {cout<<"Data<T1, int>" <<endl;}
private:
    T1 _d1;
    int _d2;
};

2. Further restrictions on parameters

Partial specialization does not just refer to specializing some parameters, but a specialized version designed to further restrict the template parameters.

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
    Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
    T1 _d1;
    T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
    Data(const T1& d1, const T2& d2)
    : _d1(d1)
    , _d2(d2)
    {
        cout<<"Data<T1&, T2&>" <<endl;
    }
private:
    const T1 & _d1;
    const T2 & _d2;
};
void test2 ()
{
    Data<double , int> d1; // 调用特化的int版本
    Data<int , double> d2; // 调用基础的模板
    Data<int *, int*> d3; // 调用特化的指针版本
    Data<int&, int&> d4(1,2); // 调用特化的指针版本
}

3. Separate compilation of templates

3.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.

3.2 Separate compilation of templates

If there is the following scenario, the declaration and definition of the template are separated, declared in the header file, and completed in the source file:

Stack.h header file: 

#include<iostream>
using namespace std;

template<class T>
T Add(const T& left, const T& right);

void func();

template<class T>
class Stack
{
public:
	void push(const T& x);
	void pop();
private:
	T* _a = nullptr;
	int _top = 0;
	int _capacity = 0;
};

Defined in the Stack.cpp source file:

//编译时不知道实例化成什么,不会实例化
template<class T>
T Add(const T& left, const T& right)
{
	cout << "T Add(const T& left, const T& right)" << endl;
	return left + right;
}

void func()
{
	cout << "void func()" << endl;
}

//类模板
template<class T>
void Stack<T>::push(const T& x)
{
	cout << "void Stack<T>::push(const T& x)" << endl;
}

template<class T>
void Stack<T>::pop()
{
	cout << "void Stack<T>::pop()" << endl;
}

test.cpp main function:

#include "Stack.h"

int main()
{
	Add(1, 2);
	//汇编时会生成 call Add<int>(地址)
	//有声明没有地址,可通过编译。有定义直接有地址

	//Stack.cpp因为Add中没有实例化,所以没有Add的地址
	func();

	Add(1.1, 2.1);//call Add<double>(?)

	Stack<int> s1;
	s1.push(1);

	Stack<double> s2;
	s2.push(1.1);

	return 0;
}

After execution, it can be found that if there is only an ordinary function like func, it can be compiled. But templates are not allowed.

This isbecause each source file .cpp is compiled separately< a i=4>. Although the Stack.cpp file has a template definition, the compiler does not see the instantiation of the Add template function in this file, so it does not generate a specific addition function< a i=6>.

In test.cpp, the compiler will look for the address of the Add function when linking, but this function is not instantiated and no specific code is generated, so an error is reported when linking a>. Class templates work for the same reason.

3.3 Solutions

1. It is also possible to put declarations and definitions in a file "xxx.hpp" or xxx.h. It is recommended to use this.
2. Explicit instantiation where the template is defined. This method is not practical and is not recommended. Because instantiating a different type requires explicit instantiation.

#include "Stack.hpp" There is a high probability that there is a template, which means there are declarations and definitions, which are expanded to the source files.

Show instantiation in Stack.cpp:

//显示示例化
template
int Add<int>(const int& left, const int& right);

template
double Add<double>(const double& left, const double& right);

//显示实例化
template
class Stack<int>;

template
class Stack<double>;

 

4. Template 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 cause 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

This article is over!

Guess you like

Origin blog.csdn.net/qq_72916130/article/details/134752365
Recommended