B.3 Overloading Details

Ru-Brd

The previous section covers most of the overloading situations encountered in everyday C++ programming. There are, unfortunately , many more rules and exceptions to these rulesmore than is reasonable to present in a book that is not really about function overloading in C++. Nonetheless, we discuss some of them here in part because they apply somewhat more often than other rules and in part to provide a sense for how deep the details go.

B.3.1 Prefer Nontemplates

When all other aspects of overload resolution are equal, a nontemplate function is preferred over an instance of a template (it doesn't matter whether that instance is generated from the generic template definition or whether it is provided as an explicit specialization). For example:

 template<typename T> int f(T);  // (1)  void f(int);  // (2)  int main()  {      return f(7);  // ERROR: selects (2), which doesn't return a value  } 

This example also clearly illustrates that overload resolution normally does not involve the return type of the selected function.

If the choice is between two templates, then the most specialized of the templates is preferred (provided one is actually more specialized than the other). See Section 12.2.2 on page 186 for a thorough explanation of this concept.

B.3.2 Conversion Sequences

An implicit conversion can, in general, be a sequence of elementary conversions. Consider the following code example:

 class Base {    public:      operator short() const;  };  class Derived : public Base {  };  void count(int);  void process(Derived const& object)  {      count(object);  // matches with user-defined conversion  } 

The call count(object) works because object can implicitly be converted to int . However, this conversion requires several steps:

  1. A conversion of object from Derived const to Base const

  2. A user-defined conversion of the resulting Base const object to type short

  3. A promotion of short to int

This is the most general kind of conversion sequence: a standard conversion (a derived-to-base conversion, in this case), followed by a user-defined conversion, followed by another standard conversion. Although there can be at most one user-defined conversion in a conversion sequence, it is also possible to have only standard conversions.

An important principle of overload resolution is that a conversion sequence that is a subsequence of another conversion sequence is preferable over the latter sequence. If there were an additional candidate function

 void count(short); 

in the example, it would be preferred for the call count(object) because it doesn't require the third step (promotion) in the conversion sequence.

B.3.3 Pointer Conversions

Pointers and pointers to members undergo various special standard conversions, including

  • Conversions to type bool

  • Conversions from an arbitrary pointer type to void*

  • Derived-to-base conversions for pointers

  • Base-to-derived conversions for pointers to members

Although all of these can cause a "match with standard conversions only," they are not ranked equally.

First, conversions to type bool (both from a regular pointer and from a pointer to a member) are considered worse than any other kind of standard conversion. For example:

 void check(void*);  // (1)  void check(bool);  // (2)  void rearrange (Matrix* m)  {      check(m);  // calls (1)    } 

Within the category of regular pointer conversions, a conversion to type void* is considered worse than a conversion from a derived class pointer to a base class pointer. Furthermore, if viable functions to different classes related by inheritance exist, a conversion to the most derived one is preferred. Here is another short example:

 class Interface {   };  class CommonProcesses : public Interface {   };  class Machine : public CommonProcesses {   };  char* serialize(Interface*);  // (1)  char* serialize(CommonProcesses*);  // (2)  void dump (Machine& machine)  {      char* buffer = serialize(machine);  // calls (2)    } 

The conversion from Machine* to CommonProcesses* is preferred over the conversion to Interface* , which is fairly intuitive.

A very similar rule applies to pointers to members: Between two conversions of related pointer-to-member types, the " closest one" in the inheritance graph (that is, the least derived) is preferred.

B.3.4 Functors and Surrogate Functions

We mentioned earlier that after the name of a function has been looked up to create an initial overload set, the set is tweaked in various ways. An interesting situation arises when a call expression refers to a class type object instead of a function. In this case there are two potential additions to the overload set.

The first addition is straightforward: Any member operator () (the function call operator) is added to the set. Objects with such operators are usually called functors (see Chapter 22).

A less obvious addition occurs when a class type object contains an implicit conversion operator to a pointer to a function type (or to a reference to a function type). [3] In such situations, a dummy (so-called surrogate ) function is added to the overload set. This surrogate function candidate is considered to have an implied parameter of the type designated by the conversion function, in addition to parameters with types corresponding to the parameter types in the destination type of that conversion function. An example makes this much clearer:

[3] The conversion operator must also be applicable in the sense that, for example, a non- const operator is not considered for const objects.

 typedef void FuncType(double, int);  class IndirectFunctor {    public:   operator()(double, double);      operator FuncType*() const;  };  void activate(IndirectFunctor const& funcObj)  {      funcObj(3, 5);  // ERROR: ambiguous!  } 

The call funcObj(3, 5) is treated as a call with three arguments: funcObj , 3 , and 5 . The viable function candidates include the member operator () (which is treated as having parameter types IndirectFunctor& , double , and double ) and a surrogate function with parameters of type FuncType* , double , and int . The surrogate function has a worse match for the implied parameter (because it requires a user-defined conversion), but it has a better match for the last parameter; hence the two candidates cannot be ordered. The call is therefore ambiguous.

Surrogate functions are in the most obscure corners of C++ and rarely occur in practice (fortunately).

B.3.5 Other Overloading Contexts

So far we have discussed overloading in the context of determining which function should be called in a call expression. However, there are a few other contexts in which a similar selection must be made.

The first context occurs when the address of a function is needed. Consider the following example:

 int n_elements(Matrix const&);  // (1)  int n_elements(Vector const&);  // (2)  void compute()  {   int (*funcPtr)(Vector const&) = n_elements;  // selects (2)    } 

Here, the name n_elements refers to an overload set, but only the address of one function in that set is desirable. Overload resolution then attempts to match the required function type (the type of funcPtr in this example) to the available candidates.

The other context that requires overload resolution is initialization . Unfortunately, this is a topic fraught with subtleties that are beyond what can be covered in an appendix. However, a simple example at least illustrates this additional aspect of overload resolution:

 #include <string>  class BigNum {    public:      BigNum(long n);      BigNum(double n);      BigNum(std::string const&);   operator double();      operator long();   };  void initDemo()  {      BigNum bn1(100103);      BigNum bn2("7057103224.095764");      int in = bn1;  } 

In this example, overload resolution is needed to select the appropriate constructor or conversion operator. In the vast majority of cases, the overloading rules produce the intuitive result. However, the details of these rules are quite complex, and some applications rely on some of the more obscure corners in this area of the C++ language (for example, the design of std::auto_ptr ).

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