c++language-lawyerimplicit-conversionoverload-resolutionreference-binding

Best viable conversion function


In the following program, which (if any) conversion function should be selected and why?

int r;
struct B {};
struct D : B {};
struct S {
  D d;
  operator D&(){r=1; return d;} // #1
  operator B&(){r=2; return d;} // #2
};
int main() {
  S s;
  B& b = s;
  return r;
}

Both gcc and clang select select the conversion function #2. But why?

The standard says:

(1) Under the conditions specified in [dcl.init.ref], a reference can be bound directly to the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “reference to cv1 T” is the type of the reference being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

(1.1) - 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 “lvalue reference to cv2 T2” (when initializing an lvalue reference or an rvalue reference to function) or “cv2 T2” or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where “cv1 T” is reference-compatible with “cv2 T2”, are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type “lvalue reference to cv2 T2” (when initializing an lvalue reference or an rvalue reference to function) or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where T2 is the same type as T or can be converted to type T with a qualification conversion, are also candidate functions.

(2) 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  ]

Here we have two candidate functions #1 and #2. Both are viable - if one of them is deleted, the program still compiles. Both conversion functions take only the implicit argument and have the same cv- and ref-qualification on it. So none should be the best viable, and the program should not compile. Why does it compile?


Solution

  • Well, as you know, overload resolution occurs in three stages: (1) enumerate candidate functions; (2) determine which candidate functions are viable; (3) select the best viable function.

    According to [over.match.best]/1:

    ... 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(F1), 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 11.6, 16.3.1.5, and 16.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 [ example ... ] or, if not that,
    • [ ... further tie-breaker rules ... ]

    The implicit conversion required from s to the implicit object parameter of either #1 or #2 is the identity conversion, so ICS1(#1) and ICS2(#1) are indistinguishable and the second bullet point is relevant here. In the case of #1, a derived-to-base conversion is required to convert from the return type of the conversion function, namely D&, to the required type, namely B&. In the case of #2, the standard conversion sequence is the identity conversion (B& to B&), which is better. Therefore, function #2 is chosen as better than #1 in this context.