12.2 Overloading Function Templates

Ru-Brd

In the previous section we saw that two function templates with the same name can coexist, even though they may be instantiated so that both have identical parameter types. Here is another simple example of this:

  // details/funcoverload.hpp  template<typename T>  int f(T)  {      return 1;  }  template<typename T>  int f(T*)  {      return 2;  } 

When T is substituted by int* in the first template, a function is obtained that has exactly the same parameter (and return) types as the one obtained by substituting int for T in the second template. Not only can these templates coexist, their respective instantiations can coexist even if they have identical parameter and return types.

The following demonstrates how two such generated functions can be called using explicit template argument syntax ( assuming the previous template declarations):

  // details/funcoverload.cpp  #include <iostream>  #include "funcoverload.hpp"  int main()  {      std::cout << f<int*>((int*)0) << std::endl;      std::cout << f<int>((int*)0)  << std::endl;  } 

This program has the following output:

 1  2 

To clarify this, let's analyze the call f<int*>((int*)0) in detail. [1] The syntax f<int*> indicates that we want to substitute the first template parameter of the template f with int* without relying on template argument deduction . In this case there is more than one template f , and therefore an overload set is created containing two functions generated from templates: f<int*>(int*) (generated from the first template) and f<int*>(int**) (generated from the second template). The argument to the call (int*)0 has type int* . This matches only the function generated from the first template, and hence that is the function that ends up being called.

[1] Note that the expression is an integer and not a null pointer constant. It becomes a null pointer constant after a special implicit conversion, but this conversion is not considered during template argument deduction.

A similar analysis can be written for the second call.

12.2.1 Signatures

Two functions can coexist in a program if they have distinct signatures. We define the signature of a function as the following information [2] :

[2] This definition is different from that given in the C++ standard, but its consequences are equivalent.

  1. The unqualified name of the function (or the name of the function template from which it was generated)

  2. The class or namespace scope of that name and, if the name has internal linkage, the translation unit in which the name is declared

  3. The const , volatile , or const volatile qualification of the function (if it is a member function with such a qualifier)

  4. The types of the function parameters (before template parameters are substituted if the function is generated from a function template)

  5. Its return type, if the function is generated from a function template

  6. The template parameters and the template arguments, if the function is generated from a function template

This means that the following templates and their instantiations could, in principle, coexist in the same program:

 template<typename T1, typename T2>  void f1(T1, T2);  template<typename T1, typename T2>  void f1(T2, T1);  template<typename T>  long f2(T);  template<typename T>  char f2(T); 

However, they cannot always be used when they're declared in the same scope because instantiating both creates an overload ambiguity. For example:

 #include <iostream>  template<typename T1, typename T2>  void f1(T1, T2)  {      std::cout << "f1(T1, T2)\n";  }  template<typename T1, typename T2>  void f1(T2, T1)  {      std::cout << "f1(T2, T1)\n";  }  // fine so far  int main()  {      f1<char, char>('a', 'b');  // ERROR: ambiguous  } 

Here, the function f1<T1 = char, T2 = char>(T1, T2) can coexist with the function f1<T1 = char, T2 = char>(T2, T1) , but overload resolution will never prefer one over the other. If the templates appear in different translation units, then the two instantiations can actually exist in the same program (and, for example, a linker should not complain about duplicate definitions because the signatures of the instantiations are distinct):

  //   Translation unit 1:  #include <iostream>  template<typename T1, typename T2>  void f1(T1, T2)  {      std::cout << "f1(T1, T2)\n";  }  void g()  {      f1<char, char>('a', 'b');  }  //   Translation unit 2:  #include <iostream>  template<typename T1, typename T2>  void f1(T2, T1)  {      std::cout << "f1(T2, T1)\n";  }  extern void g();  // defined in translation unit 1  int main()  {      f1<char, char>('a', 'b');      g();  } 

This program is valid and produces the following output:

 f1(T2, T1)  f1(T1, T2) 

12.2.2 Partial Ordering of Overloaded Function Templates

Reconsider our earlier example:

 #include <iostream>  template<typename T>  int f(T)  {      return 1;  }  template<typename T>  int f(T*)  {      return 2;  }  int main()  {      std::cout << f<int*>((int*)0) << std::endl;      std::cout << f<int>((int*)0)  << std::endl;  } 

We found that after substituting the given template argument lists ( <int*> and <int> ), overload resolution ended up selecting the right function to call. However, a function is selected even when explicit template arguments are not provided. In this case, template argument deduction comes into play. Let's slightly modify function main() in the previous example to discuss this mechanism:

 #include <iostream>  template<typename T>  int f(T)  {      return 1;  }  template<typename T>  int f(T*)  {      return 2;  }  int main()  {      std::cout << f(0) << std::endl;      std::cout << f((int*)0) << std::endl;  } 

Consider the first call ( f(0) ): The type of the argument is int , which matches the type of the parameter of the first template if we substitute T with int . However, the parameter type of the second template is always a pointer and, hence, after deduction, only an instance generated from the first template is a candidate for the call. In this case overload resolution is trivial.

The second call ( f((int*)0) ) is more interesting: Argument deduction succeeds for both templates, yielding the functions f<int*>(int*) and f<int>(int*) . From a traditional overload resolution perspective, both are equally good functions to call with an int* argument, which would suggest that the call is ambiguous (see Appendix B). However, in this sort of case an additional overload resolution criterion comes into play: The function generated from the "more specialized" template is selected. Here (as we see shortly), the second template is considered "more specialized" and thus the output of our example is (again):

 1  2 

12.2.3 Formal Ordering Rules

In our last example it may seem very intuitive that the second template is "more special" than the first because the first can accommodate just about any argument type whereas the second allows only pointer types. However, other examples are not necessarily as intuitive. In what follows , we describe the exact procedure to determine whether one function template participating in an overload set is more specialized than the other. However, note that these are partial ordering rules: It is possible that given two templates neither can be considered more specialized than the other. If overload resolution must select between two such templates, no decision can be made, and the program contains an ambiguity error.

Let's assume we are comparing two identically named function templates ft 1 and ft 2 that seem viable for a given function call. Function call parameters that are covered by a default argument and ellipsis parameters that are not used are ignored in what follows. We then synthesize two artificial lists of argument types (or for conversion function templates, a return type) by substituting every template parameter as follows:

  1. Replace each template type parameter with a unique "made up" type.

  2. Replace each template template parameter with a unique "made up" class template.

  3. Replace each nontype template parameter with a unique "made up" value of the appropriate type.

If template argument deduction of the second template against the first synthesized list of argument types succeeds with an exact match, but not vice versa, then the first template is said to be more specialized than the second. Conversely, if template argument deduction of the first template against the second synthesized list of argument types succeeds with an exact match, but not vice versa, then the second template is said to be more specialized than the first. Otherwise (either no deduction succeeds or both succeed), there is no ordering between the two templates.

Let's make this concrete by applying it to the two templates in our last example. From these two templates we synthesize two lists of argument types by replacing the template parameters as described earlier: (A1) and (A2*) (where A1 and A2 are unique made up types). Clearly, deduction of the first template against the second list of argument types succeeds by substituting A2* for T . However, there is no way to make T* of the second template match the nonpointer type A1 in the first list. Hence, we formally conclude that the second template is more specialized than the first.

Finally, consider a more intricate example involving multiple function parameters:

 template<typename T>  void t(T*, T const* = 0, ...);  template<typename T>  void t(T const*, T*, T* = 0);  void example(int* p)  {      t(p, p);  } 

First, because the actual call does not use the ellipsis parameter for the first template and the last parameter of the second template is covered by its default argument, these parameters are ignored in the partial ordering. Note that the default argument of the first template is not used; hence the corresponding parameter participates in the ordering.

The synthesized lists of argument types are (A1*, A1 const*) and (A2 const*, A2*) . Template argument deduction of (A1*, A1 const*) versus the second template actually succeeds with the substitution of T with A1 const , but the resulting match is not exact because a qualification adjustment is needed to call t<A1 const>(A1 const*, A1 const*, A1 const* = 0) with arguments of types (A1*, A1 const*) . Similarly, no exact match can be found by deducing template arguments for the first template from the argument type list (A2 const*, A2*) . Therefore, there is no ordering relationship between the two templates, and the call is ambiguous.

The formal ordering rules generally result in the intuitive selection of function templates. Once in a while, however, an example comes up for which the rules do not select the intuitive choice. It is therefore possible that the rules will be revised to accommodate those examples in the future.

12.2.4 Templates and Nontemplates

Function templates can be overloaded with nontemplate functions. All else being equal, the nontemplate function is preferred in selecting the actual function being called. The following example illustrates this:

  // details/nontmpl.cpp  #include <string>  #include <iostream>  template<typename T>  std::string f(T)  {      return "Template";  }  std::string f(int&)  {      return "Nontemplate";  }  int main()  {      int x = 7;      std::cout << f(x) << std::endl;  } 

This should output:

 Nontemplate 
Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net