Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should initialization by conversion function be ambiguous when two candidates have the same cv-qualification?

Both clang and gcc accept the following code and choose A::operator B*.

struct B
{
};

struct A : B
{
    operator A*();
    operator B*();
};

A a;
void* x = a;

My reading of the standard - specifically sentences highlighted below in bold - suggests that this conversion should be ambiguous.

Both A::operator A* and A::operator B* are candidates for overload resolution because A* and B* are both convertible to void* via a standard conversion. Because the implied object parameter A& is the only argument, only the conversion sequence that converts from the implied object argument to the implied object parameter is considered - the type yielded by the conversion function is ignored. In both cases, the implied object argument is the initializer expression's type A, and the implied object parameter is A&. If both conversion sequences are identical, there is no way to distinguish between the two candidates.

8.5 Initializers [dcl.init]

The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression.

— If the destination type is a [reference/array/class...] [deleted details not applicable to this scenario]

— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

13.3.1.5 Initialization by conversion function [over.match.conv]

Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion function can be invoked to convert an initializer expression of class type to the type of the object being initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

— The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2 X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions.

The argument list has one argument, which is the initializer expression. [ Note: This argument will be compared against the implicit object parameter of the conversion functions. —end note ]

Is this ambiguous according to the standard?

EDIT: note that this is a similar question, but not the same as Distinguishing between user-defined conversion sequences by the initial standard conversion sequence

The difference being that in my example both conversion functions have the same qualification.

like image 960
willj Avatar asked Aug 06 '13 20:08

willj


1 Answers

TLDR: When everything else is equal, overload resolution breaks the tie by which conversion function has the best conversion from its return value to the target type.


All references are to ISO/IEC 14882:2011 (C++11). The behavior of the initialization:

void* x = a;

is defined as follows. First, this is an initialization as described in 8.5 Initializers [dcl.init] and conforms to the grammar described in p1. Since the destination type void* is a non-class type, and the source type A is a class type, this specific initializer is of the form described in 8.5 p16, bullet 7:

Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

The "enumeration of applicable conversion functions" is detailed in 13.3.1.5 p1:

Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion function can be invoked to convert an initializer expression of class type to the type of the object being initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

  • The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2 X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions.

Note that both A::operator A*() and A::operator B*() are candidate functions, since both A* and B* are convertible to void* per 4.10p2: "A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”." Given that both functions are candidates, overload resolution must choose between them.

Overload resolution is decribed in 13.3 [over.match]. p2 states:

Overload resolution selects the function to call in seven distinct contexts within the language:

...

  • invocation of a conversion function for initialization of an object of a nonclass type from an expression of class type (13.3.1.5)

...

Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way. But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:

  • First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2).

  • Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

Which of our two functions are viable? 13.3.2 [over.match.viable] p1:

From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit (13.3.3).

The requirements are presented in p2:

First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.

and p3:

Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F.

Both requirements are trivially met by our conversion functions: they have a single (implicit) argument of the same type as the initializer expression a.

Determination of the best of the viable functions is described in 13.3.3 [over.match.best]. It defines some formalisms for describing conversion sequences, sequences of operations necessary to convert from the types of the actual function arguments to the types of the formal function parameters. In the case of our conversion functions, they both have exactly one parameter whose type is exactly that of the actual argument, so the "conversion sequence" corresponding to each overload is the identity sequence. They are discriminated by the language in p1:

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

  • the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type.

What about that final bullet? Does one of our overloads have a better standard conversion sequence from its return type to void*?

13.3.3.2 Ranking Implicit Conversion Sequences [over.ics.rank] p4 states in the second bullet point:

If class B is derived directly or indirectly from class A, conversion of B* to A* is better than conversion of B* to void*, and conversion of A* to void* is better than conversion of B* to void*.

This is exactly the case of the OP, except with the names A and B reversed. Overload resolution on the two conversion operators of the OP is resolved in favor of A::operator B*() since the cited rule makes the conversion sequence B*void* better than A*void*

like image 110
Casey Avatar answered Nov 19 '22 09:11

Casey