Overloading Function Templates





Item 58. Overloading Function Templates

Function templates can be overloaded with other function templates and with nontemplate functions. This capability is useful but easy to abuse.

One of the major differences between function templates and nontemplate functions is the availability of implicit conversions of actual arguments. Nontemplate functions allow a wide range of implicit conversions on their arguments, from built-in conversions (like integral promotions) to user-defined conversions (nonexplicit single argument constructors and conversion operators). In the case of function templates, because the compiler must perform argument deduction based on the types of the arguments, only trivial implicit conversions will be performed, including outer-level qualification (for example, T to const T or const T to T), reference (for example, T to T &), and array and function decay to a pointer (for example, T[42] to T *).

The practical effect of this difference is that function templates require much more exact matching than nontemplate functions do. This can be good, bad, or merely surprising. For example, consider the following:

template <typename T>
void g( T a, T b ) { ... } // this g is a template
void g( char a, char b ) { ... } // this g is not
//...
g( 12.3, 45.6 ); // template g
g( 12.3, 45 ); // non-template g!

The first call with two double arguments could be made to match the nontemplate g by converting the doubles to char implicitly (legal but inadvisable), but an exact match is available by instantiating the template g with T as double, so the template is chosen. The second call with double and int arguments will not match the template g, because the compiler will not attempt the predefined conversion from int to double on the second argument (or from double to int on the first) so as to deduce T to be double (or int). Therefore the non-member g is called, using the unfortunate predefined conversions of double and int to char.

Selecting the right version of a function when faced with a variety of template and nontemplate candidates is a complex process, and many otherwise reliable C++ compilers will select the incorrect function or issue an inappropriate error. This is also an indication that the maintainers of our code may have similar difficulties in understanding what version of an overloaded template we intended to call. For everybody's sake, when using function template overloading, keep things as simple as possible.

"Simple" doesn't imply unsophisticated. In Template Argument Deduction [57, 209], we considered a "helper" function that was used to circumvent an onerous and error-prone specialization of a complex class template:

template <typename A1, typename A2, typename R>
class PFun2 : public std::binary_function<A1,A2,R> {
    // see implementation in Template Argument
    // Deduction [57, 209] ...
};

Rather than force users to specialize this monster directly, we provided a helper function that performed template argument deduction and specialization:

template <typename R, typename A1, typename A2>
inline PFun2<A1,A2,R> makePFun( R (*pf)(A1,A2) )
    { return PFun2<A1,A2,R>(pf); }

Syntactically, this is a fairly complex piece of code, but it simplifies things for our users, allowing them to write makePFun(isGreater) rather than PFun2<int,int,bool>(isGreater) for a function declared bool isGreater(int,int).

Of course, we'll want to provide facilities for unary functions as well:

template <typename A, typename R>
class PFun1 : public std::unary_function<A,R> {
  public:
    explicit PFun1( R (*fp)(A) ) : fp_( fp ) {}
    R operator()( A a ) const
        { return fp_( a ); }
  private:
    R (*fp_)(A);
};

And a helper function:

template <typename R, typename A>
inline PFun1<A,R> makePFun( R (*pf)(A) )
    { return PFun1<A,R>(pf); }

Here is a perfect application of function template overloading. It's simple, in the sense that there is no possible confusion about which version of makePFun will be called (one is for binary functions, one for unary functions), but use of the same name for both functions makes the facility easy to learn and use.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows