The function template above introduced the basic use of function template, this article in-depth discussion of function template.

Type conversion of function template parameters

Function templates have multiple parameters of the same type, which can cause type conversion problems.

Here is an addition function template:

template <typename  T>
T add2(T a, T b) {
  std::cout << "T add2(T a, T b)" 
            << std::endl;  
  return a + b;
}
Copy the code

To use implicit instantiation, both arguments must have the same type, otherwise the compilation fails.

With display instantiation, the types of the two arguments can be different, but implicit type conversions occur.

void test1(a) {
    // Both arguments are of the same type and can be instantiated implicitly
    int a1 = 1;
    int b1 = 2;
    add2(a1, b1);
    
    // The two arguments are of different types and cannot be instantiated implicitly
    int a2 = 1;
    short b2 = 1;
    // No matching function for call to 'add2'
    // add2(a2, b2); 
  
    // Display instantiation, short is implicitly converted to int
    add2<int>(a2, b2);      
}
Copy the code

Function template overload

Function templates support overloading just like normal functions. In a function argument list, arguments are not necessarily generic, but can also be concrete types.

For example, in the addition function template, add the function template for adding three numbers. The third argument can be a generic or concrete double.

template <typename  T>
T add2(T a, T b, T c) {
    std::cout << "T add2(T a, T b, T c)" 
              << std::endl;
    return a + b + c;
}

template <typename  T>
T add2(T a, T b, double c) {
    std::cout << "T add2(T a, T b, double c)" 
      	      << std::endl;
    return a + b + c;
}
Copy the code

Limitations of function templates and solutions

Function templates may not be able to handle certain types.

For example, the following example would not work if T were a custom class FTPerson.

template <typename  T>
bool equal(T &a, T &b) {
    return a == b;
}

class FTPerson {
public:
    int age;
};
Copy the code

One solution is to provide explicit function definitions for a particular type. This is called explicit Specialization.

Display concrete stereotypes and definitions:

template<> bool equal<FTPerson>(FTPerson &p1, FTPerson &p2);

template<> bool equal<FTPerson>(FTPerson &p1, FTPerson &p2) {
    std::cout << "equal(FTPerson &p1, FTPerson &p2)" 
      	      << std::endl;
    return p1.age == p2.age;
}
Copy the code

The compiler selects the most appropriate display reification function.

void test3(a) {
    FTPerson p1;
    FTPerson p2;
    
    equal(p1, p2);	// Call the materialization function
}
Copy the code

Which function the compiler selects

If more than one function or template meets the requirements when calling a function:

  • Same function name
  • The actual participating parameters have the same number of parameters and are of the same type or can be converted implicitly

The compiler must choose the best function. The following matching rules are prioritized from high to low:

  • The functions match exactly according to the following priority
    • Common function
    • The display reification function for the template
    • A function template
  • Promotion transformation, for examplecharconvertint.floatconvertdouble
  • Standard conversion, for examplecharconvertfloat.intconvertchar.longconvertdouble

Here are five functions or templates, numbered 1 through 5. Use different numbered functions and test cases to verify matching rules.

// #1 normal function
void eat(int a) {
    std::cout << "eat(int a)" 
      	      << std::endl;
}

// #2 ordinary function
void eat(char a) {
    std::cout << "eat(char a))" 
      	      << std::endl;
}

// #3 ordinary function
void eat(float a) {
    std::cout << "eat(float a)" 
      	      << std::endl;
}

// #4 function template
template<typename T> void eat(T a) {
    std::cout << "template void eat(T a)" 
              << std::endl;
}

// #5 template display reification function
template<> void eat(int a) {
    std::cout << "template void eat(int a)" 
      	      << std::endl;
}
Copy the code

Test Case 1:

void test4(a) {
    int a = 1;
    eat(a);
}
Copy the code

The following cases are perfect matches, and the output is as follows:

  • use# 1.# 4.# 5, the compiler selects a normal function# 1, output logseat(int a)
  • use# 4.# 5, the compiler chooses to display the materialization function# 5, output logstemplate void eat(int a)

If a full match takes precedence over an promoted or standard transform, the output is as follows:

  • use# 2.# 3.# 4, the compiler selects the function template# 4, output logstemplate void eat(T a)

Test Case 2:

void test5(a) {
    char a = 'a';
    eat(a);
}
Copy the code

If the promoted conversion takes precedence over the standard conversion, the output is as follows:

  • use# 1.# 3, the compiler chooses to promote the transformation# 1, output logseat(int a)

conclusion

  • When using function templates, be aware of implicit type conversions
  • Function templates support overloading
  • Function templates cannot handle all types and can provide specific function definitions for specific types
  • If multiple functions or templates meet the requirements, the compiler selects the most appropriate function based on priority

Read the original