- 1.1 Defining function templates
- 1.2 Using function Templates
- 1.3 Two-phase Translation
- 1.3.1 Template compilation and linking issues
- 1.4 Multiple Template Parameters
- 1.4.1 Introduce additional template parameters as return value types
- 1.4.2 Let the compiler figure out the return value type itself
- 1.4.3 Declare the return value as a common type for both template parameters
- 1.5 Default Template Parameters
- 1.6 Overloading function templates
- 1.6.1 When overloading, it is better not to change the number of template parameters casually. It is better to display the specified template parameter type
- 1.6.2 Ensure that all overloaded function templates are declared when used
1.1 Defining function templates
template<typename T>
T max(T a,T b) {
return b < a ? a : b;
}
Copy the code
1.2 Using function Templates
std::cout << max(7.42) << std::endl;
std::cout << max(1.1.2.2) << std::endl;
std::cout << max("math"."mathematics") << std::endl;
Copy the code
Templates are not compiled to handle a single function of any type. Instead, the compiler generates a function for each type that uses the template. For example, a call to Max (7,42) is semantically equivalent to a call:
int max(int a,int b) {
return b < a ? a : b;
}
Copy the code
The same goes for double and string.
The process of replacing template parameters with specific parameter types is calledinstantiation
The process produces oneinstance of template
.
1.3 Two-phase Translation
Compile-time errors are reported if a particular parameter type does not support operations within the template, for example:
std::complex<float> c1,c2; // The < operation in Max is not supported, and an error is reported at compile time.max(c1,c2);
Copy the code
Templates are compiled in two stages:
- Do not template in
instantiation
thedefinition time
In this case, the template parameters are ignored and the following aspects are checked:- Syntax errors, including missing semicolons.
- Use undefined parameters.
- If static Assertion does not depend on template parameters, static Assertion is checked.
- in
instantiation
Phase, all code in the template is checked again for correctness, especially those parts that depend on template parameters.
Such as:
template<typename T>
void foo(T t) {
undeclared(a);// first-phase compile-time error if undeclared() unknown
undeclared(t); // second-phase compile-time error if undeclared(T) unknown
static_assert(sizeof(int) > 10."int too small"); // first-phase compile-time error
static_assert(sizeof(T) > 10."T too small"); // second-phase compile-time error
}
Copy the code
1.3.1 Template compilation and linking issues
Most people would organize non-template code like this:
- Put class or other type declarations in header files (.hpp,.h,.h,.hh,.hxx).
- Put function definitions, etc., in a separate compilation unit file (.cpp,.c,.c,.cc,.cxx).
But this does not work in code that contains templates, such as header files:
// myfirst.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
// declaration of template
template<typename T>
void printTypeof (T const&);
#endif // MYFIRST_HPP
Copy the code
The file that defines the function template:
// myfirst.cpp
#include <iostream>
#include <typeinfo>
#include "myfirst.hpp"
// implementation/definition of template
template<typename T>
void printTypeof (T const& x) {
std::cout << typeid(x).name() < <'\n';
}
Copy the code
Use this template in another file:
// myfirstmain.cpp
#include "myfirst.hpp"
// use of the template
int main(a) {
double ice = 3.0;
printTypeof(ice); // call function template for type double
}
Copy the code
In C/C ++, when a symbol (printTypeof) is not defined but declared at compile time, the compiler assumes that its definition is in another file, so the compiler leaves a “pit” for the linker to fill in the real symbol address.
However, as mentioned above, templates are special and need to be instantiated at compile time, i.e. template parameter type inference, template instantiation, and of course function definition. But since both CPP files are separate compilation unit files, when the compiler compiles myFirstmain.cpp, it does not find the template definition and is not instantiated.
The solution is to put the template declaration and definition in a header file. If you look at STL source files such as vector in your environment, you can put the class declaration and definition in one file.
1.4 Multiple Template Parameters
template<typename T1, typename T2>
T1 max (T1 a, T2 b) {
returnb < a ? a : b; }...auto m = max(4.7.2); // Note that the return type is the type of the first template parameter T1
Copy the code
But the problem is that the return type of Max is always T1, as noted in the comment. If we call Max (42, 66.66), the return value is 66.
There are generally three ways to solve this problem:
- Introduce additional template parameters as return value types
- Let the compiler figure out the return value type itself
- Declare the return value as the common type of the two template parameters, such as int and float, which is float
1.4.1 Introduce additional template parameters as return value types
In the derivation of function template parameter types, we generally do not specify template parameter types explicitly. However, when template parameters cannot be deduced from the passed parameters, we need to specify template parameter types explicitly.
template<typename T1, typename T2, typename RT>
RT max(T1 a, T2 b);
Copy the code
RT cannot be derived from the argument list of a function, so we need to specify it explicitly:
max<int.double.double> (4.7.2);
Copy the code
Or we can change the order of the template argument list, in which case we only need to explicitly specify a parameter type:
template<typename RT typename T1, typename T2> //RT becomes the first template argument
RT max(T1 a, T2 b); . max<double> (4.7.2);
Copy the code
1.4.2 Let the compiler figure out the return value type itself
In C++11, we can use auto and trailing return type to tell the compiler to find the return value type:
template <typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(b < a ? a : b) {
return b < a ? a : b;
}
Copy the code
Decltype will be covered later in this article, but it only needs to be known that it can get the type of the expression.
We can write it even simpler:
template <typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(true ? a : b) { // true ? a : b
return b < a ? a : b;
}
Copy the code
About? The: return value rule can refer to this: Conditional Operator:? :
See true? A: b Don’t wonder why true, the point here is not to calculate the return value, but to get the return value type.
In C++14, we can omit the trailing return type:
template<typename T1, typename T2>
auto max (T1 a, T2 b) {
return b < a ? a : b;
}
Copy the code
1.4.3 Declare the return value as a common type for both template parameters
The new c++11 feature STD ::common_type can produce several different types of common types.
template <typename T1, typename T2>
typename std::common_type<T1, T2>::type max(T1 a, T2 b) {
return b < a ? a : b;
}
Copy the code
In c++14, it is easier to write:
template <typename T1, typename T2>
std::common_type_t<T1, T2> max(T1 a, T2 b) {
return b < a ? a : b;
}
Copy the code
Using the _t suffix saves us from writing typename and ::type. Similarly, _v is common in c++14’s type traits.
1.5 Default Template Parameters
This looks like the default argument to a function.
template <typename T1, typename T2, typename RT = std::common_type_t<T1, T2>>
RT max(T1 a, T2 b) {
return b < a ? a : b;
}
auto a = max(4.7.2);
auto b = max<double.int.long double> (7.2.4);
Copy the code
As in the second usage, if we want to display the type of RT, we must display all three parameter types. But unlike the function default argument, we can put the default argument in the first place:
template <typename RT = long.typename T1, typename T2>
RT max(T1 a, T2 b) {
return b < a ? a : b;
}
int i;
longl; ...max(i, l); // Return value type is long (RT default)
max<int> (4.42); // Returns int because it is explicitly specified
Copy the code
1.6 Overloading function templates
This is similar to normal function overloading:
// maximum of two int values:
int max(int a, int b) {
return b < a ? a : b;
}
// maximum of two values of any type:
template <typename T>
T max(T a, T b) {
return b < a ? a : b;
}
int main(a) {
max(7.42); // calls the nontemplate for two ints
max(7.0.42.0); // calls max<double> (by argument deduction)
max('a'.'b'); // calls max<char> (by argument deduction)
max<>(7.42); // calls max<int> (by argument deduction)
max<double> (7.42); // calls max<double> (no argument deduction)
max('a'.42.7); // calls the nontemplate for two ints
}
Copy the code
The last Max (‘a’, 42.7) needs to be explained here. Because automatic type conversions are not allowed during template argument type derivation, but normal function calls are allowed, this calls non-template functions.
Ps. Function templates are not specialized like class templates because they are overloaded.
There are two more basic rules to know about overloading:
1.6.1 When overloading, it is better not to change the number of template parameters casually. It is better to display the specified template parameter type
Here is the offending code:
// maximum of two values of any type (call-by-reference)
template <typename T> T const &max(T const &a, T const &b) {
return b < a ? a : b;
}
// maximum of two C-strings (call-by-value)
char const *max(char const *a, char const *b) {
return std::strcmp(b, a) < 0 ? a : b;
}
// maximum of three values of any type (call-by-reference)
template <typename T> T const &max(T const &a, T const &b, T const &c) {
return max(max(a, b), c); // error if max(a,b) uses call-by-value
}
int main(a) {
auto m1 = max(7.42.68); // OK
char const *s1 = "frederic";
char const *s2 = "anica";
char const *s3 = "lucas";
auto m2 = max(s1, s2, s3); // run-time ERROR
}
Copy the code
Return Max (Max (a,b), c); Because char const * Max (char const *a, char const *b) is passed by value, Max (a,b) produces an address pointing to the destroyed stack frame, which results in undefined behavior.
1.6.2 Ensure that all overloaded function templates are declared when used
// maximum of two values of any type:
template <typename T>
T max(T a, T b) {
std::cout << "max<T>()\n";
return b < a ? a : b;
}
// maximum of three values of any type:
template <typename T>
T max(T a, T b, T c) {
return max(max(a, b), c);
}
// maximum of two int values:
int max(int a, int b) {
std::cout << "max(int,int) \n";
return b < a ? a : b;
}
int main(a) {
max(47.11.33); // max<T>()
}
Copy the code
This is easy to understand.
(after)
Friends can follow my public account, get the most timely updates: