Argument Matching and Conversions

When the compiler tries to match actual arguments against the arguments in function declarations, it can supply standard or user-defined conversions to obtain the correct type if no exact match can be found. The application of conversions is subject to these rules:

Sequences of conversions that contain more than one user-defined conversion are not considered.

Sequences of conversions that can be shortened by removing intermediate conversions are not considered.

The resultant sequence of conversions, if any, is called the best matching sequence. There are several ways to convert an object of type int to type unsigned long using standard conversions (described in Chapter 3, “Standard Conversions”):

Convert from int to long, then from long to unsigned long.

Convert from int to unsigned long.

The first sequence, while it achieves the desired goal, is not the best matching sequence—a shorter sequence exists.

Table 12.1 shows a group of conversions, called trivial conversions, that have a limited effect on the determination of which sequence is the best matching. The instances in which trivial conversions affect choice of sequence are discussed in the list following the table.

Table 12.1 Trivial Conversions

Convert from Type Convert to Type

type-name type-name&
type-name& type-name
type-name[] type-name*
type-name( argument-list ) (*type-name)( argument-list )
type-name const type-name
type-name volatile type-name
type-name* const type-name*
type-name* volatile type-name*

The sequence in which conversions are attempted is as follows:

1.Exact match. An exact match between the types with which the function is called and the types declared in the function prototype is always the best match. Sequences of trivial conversions are classified as exact matches. However, sequences that do not make any of these conversions are considered better than sequences that convert:

From pointer, to pointer to const (type * to const type *).

From pointer, to pointer to volatile (type * to volatile type *).

From reference, to reference to const (type & to const type &).

From reference, to reference to volatile (type & to volatile type &).

2.Match using promotions. Any sequence not classified as an exact match that contains only integral promotions, conversions from float to double, and trivial conversions is classified as a match using promotions. While not as good a match as any exact match, a match using promotions is better than a match using standard conversions.

3.Match using standard conversions. Any sequence not classified as an exact match or a match using promotions that contains only standard conversions and trivial conversions is classified as a match using standard conversions. Within this category, the following rules are applied:

Conversion from a pointer to a derived class, to a pointer to a direct or indirect base class is preferable to converting to void * or const void *.

Conversion from a pointer to a derived class, to a pointer to a base class produces a better match the closer the base class is to a direct base class. Suppose the class hierarchy is as shown in Figure 12.1.

Conversion from type D* to type C* is preferable to conversion from type D* to type B*. Similarly, conversion from type D* to type B* is preferable to conversion from type D* to type A*.

This same rule applies to reference conversions. Conversion from type D& to type C& is preferable to conversion from type D& to type B&, and so on.

This same rule applies to pointer-to-member conversions. Conversion from type T D::* to type T C::* is preferable to conversion from type T D::* to type T B::*, and so on (where T is the type of the member).

The preceding rule applies only along a given path of derivation. Consider the graph shown in Figure 12.2.

Conversion from type C* to type B* is preferable to conversion from type C* to type A*. The reason is that they are on the same path, and B* is closer. However, conversion from type C* to type D* is not preferable to conversion to type A*; there is no preference because the conversions follow different paths.

Match with user-defined conversions. This sequence cannot be classified as an exact match, a match using promotions, or a match using standard conversions. The sequence must contain only user-defined conversions, standard conversions, or trivial conversions to be classified as a match with user-defined conversions. A match with user-defined conversions is considered a better match than a match with an ellipsis, but not as good a match as a match with standard conversions.Match with an ellipsis. Any sequence that matches an ellipsis in the declaration is classified as a match with an ellipsis. This is considered the weakest match.

User-defined conversions are applied if no built-in promotion or conversion exists. These conversions are selected on the basis of the type of the argument being matched. Consider the following code:

class UDC

{

public:

operator int();

operator long();

};

void Print( int i );

...

UDC udc;

Print( udc );

The available user-defined conversions for class UDC are from type int and type long. Therefore, the compiler considers conversions for the type of the object being matched: UDC. A conversion to int exists, and it is selected.

During the process of matching arguments, standard conversions can be applied to both the argument and the result of a user-defined conversion. Therefore, the following code works:

void LogToFile( long l );

...

UDC udc;

LogToFile( udc );

In the preceding example, the user-defined conversion, operator long, is invoked to convert udc to type long. If no user-defined conversion to type long had been defined, the conversion would have proceeded as follows: Type UDC would have been converted to type int using the user-defined conversion. Then the standard conversion from type int to long would have been applied to match the argument in the declaration.

If any user-defined conversions are required to match an argument, the standard conversions are not used when evaluating the best match. This is true even if more than one candidate function requires a user-defined conversion; in such a case, the functions are considered equal. For example:

class UDC1

{

public:

UDC1( int ); // User-defined conversion from int.

};

class UDC2

{

public:

UDC2( long ); // User-defined conversion from long.

};

...

void Func( UDC1 );

void Func( UDC2 );

...

Func( 1 );

Both versions of Func require a user-defined conversion to convert type int to the class type argument. The possible conversions are:

Convert from type int to type UDC1 (a user-defined conversion).

Convert from type int to type long, then convert to type UDC2 (a two-step conversion).

Even though the second of these requires a standard conversion, as well as the user-defined conversion, the two conversions are still considered equal.

Note:

User-defined conversions are considered conversion by construction or conversion by initialization (conversion function). Both methods are considered equal when considering the best match.