[C + +] Chapter 10: class template - full specialization and partial specialization of templates and separate compilation of templates
1, Non type template parameters
Template parameters are classified into two types: type parameter and non type parameter.
Type parameter: the parameter type name that appears in the template parameter list and is marked with the keyword class or typename, that is, the most common parameter in the template parameter list.
Non type parameter: a constant is used as a parameter of the class (function) template. This method is not commonly used, so you need to pay attention. Note: constants are usually of integer type.
for instance:
template<class T, size_t N = 10> // The first parameter is of type T, and the second parameter is an integer value with a default value class Array { public: T array[N]; // You can use the parameters in the template parameter list };
be careful:
1. In the template parameter list, except for the class and typename keywords, only integer type parameters can be used as constants, while floating point numbers, class objects, etc. cannot be used as template parameters.
2. The result of non type template parameters must be determined by the compiler, that is, the template cannot be compiled separately (described later).
Two. Template specialization
1. Why is there template specialization?
First, before we use template specialization, we need to find out why template specialization occurs, that is, what problems do template specialization appear to solve?
Let's first write an example to see if there are some problems with the template written in this way.
template<class T> bool equal(const T& a, const T& b) { return a == b; } int main() { int a = 10, b = 10, c = 20; cout << equal(a, b) << endl; // Output 1 indicates true cout << equal(a, c) << endl; // Output 0, indicating false char s1[] = "hello"; char s2[] = "hello"; cout << equal(s1, s2) << endl; // Output 0, indicating false,??? return 0; }
In the above example, a relatively equal function template is written with the template. At the same time, I also wrote a test code.
When comparing two integers, the output can be normal. However, when comparing two char [] arrays, the output answer is different from our expectation.
This is because the array name represents the address of the array. Although the contents (hello string) stored in the two char arrays are the same, the addresses of the two arrays are compared during comparison. If the addresses are compared, the memory opened up by the two arrays on the stack must be in different places, so the addresses of the arrays must be different, so false is output.
To solve this problem, we can only use strcmp, a function in the c standard library, to compare the contents of character arrays. However, in addition to string arrays, when comparing other contents, you usually use = = comparison. What should I do?
(answer:) in order to solve the special case in generic programming such as template, we can use the specialization of template, in other words, we can manually rewrite the code for a specific type parameter in the template. This special treatment in generic programming is called template specialization.
2. Specialization and partial specialization of formwork
There are two kinds of templates: function template and class template, so the template has the specialization of function template and class template. However, the two forms of template specialization are similar.
2.1 specialization of function template
Steps for function template specialization:
- You must have a basic function template first
- The keyword template is followed by a pair of empty angle brackets < >
- The function name is followed by a pair of angle brackets, which specify the type to be specialized
- Function parameter table: must be exactly the same as the basic parameter type of the template function
Let's continue with the example of comparing function templates above.
//For specialization of char * type template<> bool equal<const char* const>(const char* const &a, const char* const &b) { return strcmp(a, b) == 0; }
**Add: * * there are not many cases where a general function template needs to be specialized, but if you want to implement a specialized version, it will be a little more complex than an ordinary function template. Therefore, we do not apply to the specialization of general function templates. Because the feature of the function is that it will give priority to calling functions with more matching function parameters, we can use a specific function to replace the specialized version of the function template.
Namely:
template<class T> bool equal(const T& a, const T& b) { return a == b; } bool equal(char* a, char* b) { return strcmp(a, b); } int main() { char a[] = "hello"; char b[] = "hello"; cout << equal(a, b) << endl; // Call the entity's function first, not the function template return 0; }
2.2 specialization and partial specialization of class 2 formwork
Because the problems often handled in class templates are complex, you will also encounter the result of template specialization.
for instance:
template<class T1, class T2> class Complex { public: Complex() { cout << "<T1, T2>" << endl; } private: T1 a; T2 b; }; template<> class Complex<int, int> { // Specialized version of < int, int > public: Complex() { cout << "<int, int>" << endl; } private: int a; int b; }; template<> class Complex<int ,double> { // Specialized version of < int, double > public: Complex() { cout << "<int, double>" << endl; } private: int a; double b; }; int main() { Complex<char, char>(); // Output < T1, T2 > Complex<int, int>(); // Output < int, int > Complex<int, double>();// Divisor < int, double > return 0; }
In addition to the specialization of templates, there are partial specialization of class templates (function templates also exist, but because the specialization and partial specialization of function templates are not commonly used, there are no examples). If you understand the specialization of the template, you can better understand the partial specialization of the template, that is, not all the parameters in the template parameter list are specialized, but some parameters in the list need special processing, or for a T-type parameter, the pointer t * or the special version of T & is referenced.
for instance:
1. Partial specialization
Replace some type parameters in the parameter list of the template with specific types.
template<class T> class Complex<T, int> { public: Complex() { cout << "<T, int>" << endl; } private: T a; int b; } int main() { Complex<char, int>(); // Output < T, int > return 0; }
2. Parameter limits
Replace the generic parameter t in the template's parameter list with reference type T & or pointer type T *. (Note: T & or T * only restricts the type, not a new type, but it is also a specialized class)
template<class T1, class T2> class Complex<T1&, T2&> { public: Complex() { cout << "<T1&, T2&>" << endl; } private: T1 a; T2 b; }; template<class T1, class T2> class Complex<T1*, T2*> { public: Complex() { cout << "<T1*, T2*>" << endl; } private: T1 a; T2 b; }; int main() { Complex<int*, int*>(); // Output < T1 *, T2 * > Complex<int&, int&>(); // Output < T1 &, T2& return 0; }
Three. Template separation and compilation
1. What is separate compilation?
A program (project) is implemented by several source files, and each source file is compiled separately to generate the target file. Finally, the process of linking all the target files to form a single executable file is called separate compilation mode.
for instance:
//---------------test.h----------------// void func();//Here we declare a function f //---------------test.cpp--------------// #include"test.h" void func() { cout << "hello world" << endl;//The func function declared in test.h is implemented here } //---------------main.cpp--------------// #include"test.h" int main() { func(); //Call func, which has an external connection type }
2. Why can't analysis templates be compiled separately
To find out why the template cannot be compiled separately, we must find out what is done in each step of laborious compilation?
Here, we will use the above example to explain what happens in separate compilation, and then compare it to explain why templates cannot be separated compilation.
First, we need to know that the program needs to go through four steps:
1. Preprocessing: header file expansion, de annotation, macro replacement, conditional compilation, etc.
2. Compilation: check whether the code has syntax errors. If there are no errors, convert the code into assembly code.
3. Assembly: convert assembly code into binary machine code and generate. obj file (generate. o file under Linux)
4. Link: link all. obj files together to generate an executable program.
In the above conditional compilation example of test.h, test. CPP and main. CPP. Both test.cpp and main.cpp generate. obj files after assembly. When main.cpp generates the. obj file, there is no code for the func function in test.cpp in main.cpp. It can only be called when the two. obj files are linked together. Before that, there was only the declaration of func function in main.cpp. That is, tell the compiler not to report errors first. When you link, you will find the implementation of the function. Therefore, when the assembly code is formed at the bottom, the instruction calling the function is the call command, which can only call?????, there???? It is the address of the func function waiting to be implemented. When the compiler finds the function when it is linked, it is the address of call func.
After understanding the general conditional compilation, here is the conditional compilation of Kangkang template:
Or which example illustrates:
//-------------test.h----------------// template<class T> class A { public: void func(); // This is just a statement }; //---------------test.cpp-------------// #include"test.h" template<class T> void A<T>::func() // Implementation of template { cout << "hello world" << endl; } //---------------main.cpp---------------// #include"test.h" int main() { A<int> a; a.func(); // Call func() function }
The reason why the template cannot be conditionally compiled is that after preprocessing, compilation and assembly, the template can only generate the main.obj object file, but not the test.obj object file.
This is because if a template cannot be instantiated and called, the template will not compile a template class. This is a clear indication of the C + + standard that when a template is not used, it should not be instantiated
There is no instantiation in the test.cpp file, so the template will not actively generate a template class. Therefore, when linking, the call instruction in main.cpp failed to find the address of func() function in other. obj files, resulting in the failure to successfully call func function in the end, so the executable program cannot be linked.
Summary reason: the place defined in test.cpp cannot be instantiated, and there is no function definition in the place declared in main.cpp. Therefore, an executable program cannot be formed
3. How to solve the problem that templates cannot be compiled separately
There are two ways to solve this problem:
Method 1: you can actively instantiate in test.cpp, which is where the function is defined. That is, if you want to call a function of type < int >, it will be instantiated.
template<> class A<int> { public: void func() { cout << "hello world" << endl; } };
However, it is generally not recommended to write in this way, because it is too troublesome, which returns to the original state of not using template classes. It is still written manually one by one.
Method 2: you can directly implement the function in test.h, that is, where the template is declared, which is equivalent to directly copying all the code to main.cpp during the original processing, so that the template can be instantiated. (recommended. It is written like this in STL library)
4, Summary of templates
advantage:
- The concept of template is used in STL library, which makes the template reuse code, save resources and faster iterative development.
- Enhanced code flexibility.
Defects:
- Templates can cause code bloat problems and lead to longer compilation times.
- When a template compilation error occurs, the error information is very messy and it is difficult to locate the error.