[C++] Template advancement (non-type template parameters, specialization, separate compilation)

Insert image description here

1. Non-type template parameters

Template parameter classification type formal parameter and non-type formal parameter.
The type parameter appears 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.

Let’s take a look at an example:

// 定义一个模板类型的静态数组
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;
}

Note:
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. Specialization of templates

2.1 What is template specialization?

Normally,using templates can implement some type-independent code, but for some special types, you may get some wrong results, requires special processing, for example: a function template specifically used for less than comparison is implemented, the code is as follows:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    
    
	return left < right;
}
int main()
{
    
    
    cout << Less(1, 2) << endl; // 可以比较,结果正确
    
    return 0;
}

Insert image description here

We can also compare here the date class objects we wrote before:
Insert image description here

Let’s look at this again:

Date d1(2023, 1, 1);
Date d2(2023, 2, 2);
cout << Less(d1, d2) << endl;

Date* pd1 = &d1;
Date* pd2 = &d2;
cout << Less(pd1, pd2) << endl;

Insert image description here

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 point, it is necessary to specialize the template. 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 change.

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

Let’s follow these steps now to deal with the problem we just made.

template<class T>
bool Less(T left, T right)
{
    
    
    return left < right;
}
// 日期类指针比较的模板特化
template <>
bool Less<Date*>(Date* left, Date* right)
{
    
    
    return *left < *right;
}

int main()
{
    
    
    Date d1(2023, 1, 1);
    Date d2(2023, 2, 2);
    cout << Less(d1, d2) << endl;

    Date* pd1 = &d1;
    Date* pd2 = &d2;
    cout << Less(pd1, pd2) << endl;

    return 0;
}

Insert image description here

The specialized function template will be correct when compared.
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.

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

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

2.3 Instantiation of class templates

2.3.1 All specializations

Full specialization means that all parameters in the template parameter list are deterministic.
Let’s start with the full specialization of the custom Date class:

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

private:
    T1 _d1;
    T2 _d2;
};

At this time, no matter what type of object we instantiate, the template can help us instantiate it, but now I want to accurately match the int and char type Data objects. At this time, we must specialize an int and char version of Data. When the class comes out, full specialization is required at this time.

template <>
class Data<int, char>
{
    
    
public:
    Data() 
    {
    
     
        cout << "Data<int, char>" << endl; 
    }

private:
    int _d1;
    char _d2;
};

Insert image description here

2.3.2 Partial specialization

Partial specialization: Any specialization that further restricts the design of template parameters.

Partial specialization has two forms:

  1. Partial specialization – specialize a part of the parameters in the template parameter class table
  2. Further restrictions on parameters - Partial specialization does not just mean specializing some parameters, but a specialized version designed to further restrict the template parameters.

1. Partial specialization
Suppose I need to specialize the second parameter of the Data class to int

template <class T>
class Data<T, int>
{
    
    
public:
    Data() 
    {
    
     
        cout << "Data<T, int>" << endl; 
    }

private:
    T _d1;
    int _d2;
};

Insert image description here

2. Further restrictions on parameters
Two parameters are specialized into pointer types

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

private:
    T1* _d1;
    T2* _d2;
};

Insert image description here

Two parameters are partially specialized to reference types

template <class T1, class 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;
};

Insert image description here

Note: Priority: Full Specialization > Half Specialization > Default

3. Template separation compilation

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:

// 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;
}

Analysis: To run a C/C++ program, you generally need to go through the following steps: Preprocessing-> Compilation-> Assembly-> Link
Compilation: Compile the program according to the language characteristics Carry out lexical, grammatical, and semantic analysis, and generate assembly code after error checking.Note that header files do not participate in compilation. The compiler compiles multiple source files in the project separately.

Linking: Merge multiple obj files into one and deal with unresolved address issues.
Insert image description here

3.3 Solutions

  1. It is also possible to put declarations and definitions in a file "xxx.hpp" or xxx.h. This is recommended.
  2. is explicitly instantiated where the template is defined. This method is not practical and is not recommended.

Place the declaration and definition in .h/.hpp. During preprocessing, the header file will be expanded in the main.c file, and the template will be instantiated.

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

shortcoming:

  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/Ljy_cx_21_4_3/article/details/134940783