26

The question arose while I was researching the answer to this SO question. Consider the following code:

struct A{
    operator char() const{ return 'a'; }
    operator int() const{ return 10; }
};

struct B {
    void operator<< (int) { }
};

int main()
{
    A a;
    B b;
    b << a;
}

The conversion of a to int can be either via a.operator char() followed by an integral promotion, or a.operator int() followed by an identity conversion (i.e., no conversion at all). The standard says that (§13.3.3.1 [over.best.ics]/p10, footnote omitted, bolding mine; all quotes are from N3936):

If several different sequences of conversions exist that each convert the argument to the parameter type, the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence designated the ambiguous conversion sequence. For the purpose of ranking implicit conversion sequences as described in 13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other user-defined conversion sequence. If a function that uses the ambiguous conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion of one of the arguments in the call is ambiguous.

Here, B::operator<<(int) is the only viable candidate - and hence is the best viable candidate, even though the conversion sequence for the parameter is the ambiguous conversion sequence. According to the bolded sentence, then, the call should be ill-formed because "the conversion of one of the arguments in the call is ambiguous".

Yet no compiler that I tested (g++, clang, and MSVC) actually reports an error, which makes sense because after the function to call is selected through overload resolution, the function's "parameter (8.3.5) shall be initialized (8.5, 12.8, 12.1) with its corresponding argument" (§5.2.2 [expr.call]/p4). This initialization is copy-initialization (§8.5 [dcl.init]/p15), and according to §8.5 [dcl.init]/p17, results in a new round of overload resolution to determine the conversion function to use:

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 initializer is not a single (possibly parenthesized) expression, the source type is not defined.

  • [...]
  • If the destination type is a (possibly cv-qualified) class type: [...]
  • 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.
  • [...]

And in this round of overload resolution, there is a tiebreaker in §13.3.3 [over.match.best]/p1:

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.

(Example and remainder of the list omitted)

Since the standard conversion sequence from int to int (Exact Match rank) is better than the standard conversion sequence from char to int (Promotion rank), the first beats the second, and there should be no ambiguity - the conversion defined by operator int() will be used for the initialization, which then contradicts the sentence in §13.3.3.1 [over.best.ics]/p10 that says the function call will be ill-formed because of ambiguity.

Is there anything wrong in the above analysis, or is that sentence a bug in the standard?

Community
  • 1
  • 1
T.C.
  • 123,516
  • 14
  • 264
  • 384
  • Just as another data point, if you change the operator to accept `long` do you get the expected ambiguous call? – Mark B Aug 20 '14 at 01:39
  • @MarkB `operator< – T.C. Aug 20 '14 at 01:47
  • It seems to me there's something missing in [over.best.ics]/p10, along the lines of "If several different sequences of conversions exist *and at least between two, none is better*". The rationale of the ambiguity rule given in the footnote still applies under that change. – dyp Aug 20 '14 at 23:52
  • @dyp One user-defined conversion sequence can be better than another only if they use the same user-defined conversion function ([over.ics.rank]/p3, "User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if **they contain the same user-defined conversion function or constructor** [...] and [...] the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2."). The special rule in [over.match.best]/p1 applies only in the initialization context. – T.C. Aug 20 '14 at 23:59
  • Oh, right. Hmmm. If you *add* another overload `operator<< (long)`, clang++ and g++ complain about an ambiguity for `b << a`, that disappears when removing `operator char()`. – dyp Aug 21 '14 at 00:02
  • The rule seems not to be applied when a second overload exists that is not viable. I.e., `operator<< (B)` + `operator<< (int)` and `operator int()` + `operator char()` is accepted, while `operator<< (long)` + `operator<< (int)` is rejected as ambiguous. So it seems that the rule is applied after selecting the viable overloads. Maybe the solution therefore lies in "is selected as the best viable function". If the set consists of only one function, no selection has to be performed (in theory). – dyp Aug 21 '14 at 00:12
  • @dyp We can trivially make `B` a template and write a template non-member `operator< &, char)`. Then you get two viable functions, the non-template member `operator< – T.C. Aug 21 '14 at 00:24
  • @dyp Or not even go that far, and just add a member `operator<< (int) const`. Both are viable, non-const version is selected over const version, but no ambiguity. – T.C. Aug 21 '14 at 00:29
  • Huh? That is interesting. We essentially have `void foo(B&, int)` vs `void foo(B const&, int)` (not ambiguous). OTOH, `foo(B const&, int)` vs `foo(B&, long)` and `foo(B&, int)` vs `foo(B&, long)` are ambiguous, but not `foo(B&, int)` + `foo(B const&, long)`. – dyp Aug 21 '14 at 00:44
  • @dyp Right, `B&` beats `const B&` when a non-const `B` is passed. For the second parameter, neither one beats the other. So for `foo(B const&, int) vs foo(B&, long)`, the second one is picked, but then the conversion to `long` is ambiguous. For `foo(B&, int) vs foo(B&, long)`, it can't pick one at all. – T.C. Aug 21 '14 at 00:54
  • I think it applies when there is overloading, not just a single function that it needs to get the arguments to match on. Since operator<< is defined as a member, the compiler (rightly or wrongly) might just take it, like it would a non-operator member call. After all, it can't convert the left-hand-argument other than base class inheritence, and those are hidden. The whole point of the rule is to improve upon silly "not really" conflicts, not introduce more of them. – JDługosz Aug 30 '14 at 07:46
  • @jdlugosz It's trivial to add an extra overload that's viable but a worse match. See my discussion with dyp right above. – T.C. Aug 30 '14 at 07:48
  • @TC Actualy [your demo](http://goo.gl/UFp1hf) passes because the external template must be explicitly called to exist (instantiated). Change that to `void operator<< (B &, char) { g(); }` and you will see the ambiguity raising again. – Sérgio Castelani Sep 25 '14 at 20:07
  • @SérgioCastelani No, it compiles because non-templates are preferred over templates in overload resolution when they are otherwise equally as good. Regardless, the question isn't about which `operator < – T.C. Sep 25 '14 at 22:44

1 Answers1

8

When deciding the best user-defined conversion for a user-defined conversion sequence we have an overload candidate set.

§13.3.3/p1 says:

Define ICSi(F) as follows:

  • [...]

  • let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.

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

— [...]

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.

This applies since

§13.3.3.1.2/p2

2 The second standard conversion sequence converts the result of the user-defined conversion to the target type for the sequence. Since an implicit conversion sequence is an initialization, the special rules for initialization by user-defined conversion apply when selecting the best user-defined conversion for a user-defined conversion sequence (see 13.3.3 and 13.3.3.1).

and therefore the conversion sequence involving operator int is selected as best match.

Finally I would rephrase §13.3.3.1/p10 as

If several different sequences of conversions exist that each convert the argument to the parameter type and it was not possible to determine a best candidate, the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence designated the ambiguous conversion sequence. For the purpose of ranking implicit conversion sequences as described in 13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other user-defined conversion sequence134. If a function that uses the ambiguous conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion of one of the arguments in the call is ambiguous.

Marco A.
  • 41,192
  • 25
  • 117
  • 233
  • 1
    If I'm reading your answer correctly, the argument is that the overload resolution to select the user-defined conversion sequence to use actually happens during (rather than after) the overload resolution for `operator < – T.C. Sep 30 '14 at 21:03
  • Yes, there are two rounds of overload resolution involved in this decision process. In case the result is the ambiguous conversion sequence, the ambiguous candidate set (of viable functions) is added to the ICS. – Marco A. Sep 30 '14 at 21:41
  • 1+ for nice explanation, I am feeling like neo, I can see code matrix now :) – Angelus Mortis Mar 14 '16 at 19:58