2.4 Overloading Function Templates

Ru-Brd

Like ordinary functions, function templates can be overloaded. That is, you can have different function definitions with the same function name so that when that name is used in a function call, a C++ compiler must decide which one of the various candidates to call. The rules for this decision may become rather complicated, even without templates. In this section we discuss overloading when templates are involved. If you are not familiar with the basic rules of overloading without templates, please look at Appendix B, where we provide a reasonably detailed survey of the overload resolution rules.

The following short program illustrates overloading a function template:

  // basics/max2.cpp   // maximum of two  int  values  inline int const& max (int const& a, int const& b)  {      return a<b?b:a;  }  // maximum of two values of any type  template <typename T>  inline T const& max (T const& a, T const& b)  {      return a<b?b:a;  }  // maximum of three values of any type  template <typename T>  inline T const& max (T const& a, T const& b, T const& c)  {      return max (max(a,b), c);  }  int main()  {      ::max(7, 42, 68);  // calls the template for three arguments  ::max(7.0, 42.0);  // calls  max<double>  (by argument deduction)  ::max('a', 'b');  // calls  max<char>  (by argument deduction)  ::max(7, 42);  // calls the nontemplate for two  int  s  ::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  int  s  } 

As this example shows, a nontemplate function can coexist with a function template that has the same name and can be instantiated with the same type. All other factors being equal, the overload resolution process normally prefers this nontemplate over one generated from the template. The fourth call falls under this rule:

 max(7, 42)  // both  int  values match the nontemplate function perfectly  

If the template can generate a function with a better match, however, then the template is selected. This is demonstrated by the second and third call of max() :

 max(7.0, 42.0)  // calls the  max<double>  (by argument deduction)  max('a', 'b');  // calls the  max<char>  (by argument deduction)  

It is also possible to specify explicitly an empty template argument list. This syntax indicates that only templates may resolve a call, but all the template parameters should be deduced from the call arguments:

 max<>(7, 42)  // calls  max<int>  (by argument deduction)  

Because automatic type conversion is not considered for templates but is considered for ordinary functions, the last call uses the nontemplate function (while 'a' and 42.7 both are converted to int ):

 max('a', 42.7)  // only the nontemplate function allows different argument types  

A more useful example would be to overload the maximum template for pointers and ordinary C-strings:

  // basics/max3.cpp  #include <iostream>  #include <cstring>  #include <string>  // maximum of two values of any type  template <typename T>  inline T const& max (T const& a, T const& b)  {      return a < b ? b : a;  }  // maximum of two pointers  template <typename T>  inline T* const& max (T* const& a, T* const& b)  {      return *a < *b ? b : a;  }  // maximum of two C-strings  inline char const* const& max (char const* const& a,                                 char const* const& b)  {      return std::strcmp(a,b) < 0 ? b : a;  }  int main ()  {      int a=7;      int b=42;      ::max(a,b);  //  max()  for two values of type  int      std::string s="hey";      std::string t="you";      ::max(s,t);  //  max()  for two values of type  std::string      int* p1 = &b;      int* p2 = &a;      ::max(p1,p2);  //  max()  for two pointers  char const* s1 = "David";      char const* s2 = "Nico";      ::max(s1,s2);  //  max()  for two C-strings  } 

Note that in all overloaded implementations , we pass all arguments by reference. In general, it is a good idea not to change more than necessary when overloading function templates. You should limit your changes to the number of parameters or to specifying template parameters explicitly. Otherwise, unexpected effects may happen. For example, if you overload the max() template, which passes the arguments by reference, for two C-strings passed by value, you can't use the three-argument version to compute the maximum of three C-strings:

  // basics/max3a.cpp  #include <iostream>  #include <cstring>  #include <string>  // maximum of two values of any type (call-by-reference)  template <typename T>  inline T const& max (T const& a, T const& b)  {      return a < b ? b : a;  }  // maximum of two C-strings (call-by-value)  inline char const* max (char const* a, char const* b)  {      return std::strcmp(a,b) < 0 ? b : a;  }  // maximum of three values of any type (call-by-reference)  template <typename T>  inline 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 ()  {      ::max(7, 42, 68);  // OK  const char* s1 = "frederic";      const char* s2 = "anica";      const char* s3 = "lucas";      ::max(s1, s2, s3);  // ERROR  } 

The problem is that if you call max() for three C-strings, the statement

 return max (max(a,b), c); 

becomes an error. This is because for C-strings, max(a,b) creates a new, temporary local value that may be returned by the function by reference.

This is only one example of code that might behave differently than expected as a result of detailed overload resolution rules. For example, the fact that not all overloaded functions are visible when a corresponding function call is made may or may not matter. In fact, defining a three-argument version of max() without having seen the declaration of a special two-argument version of max() for int s causes the two-argument template to be used by the three-argument version:

  // basics/max4.cpp   // maximum of two values of any type  template <typename T>  inline T const& max (T const& a, T const& b)  {      return a<b?b:a;  }  // maximum of three values of any type  template <typename T>  inline T const& max (T const& a, T const& b, T const& c)  {      return max (max(a,b), c);  // uses the template version even for  int  s  }  // because the following declaration comes   // too late:   // maximum of two  int  values  inline int const& max (int const& a, int const& b)  {      return a<b?b:a;  } 

We discuss details in Section 9.2 on page 121, but for the moment, as a rule of thumb you should always have all overloaded versions of a function declared before the function is called.

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