This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 118e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2025-11-05
[Accepted as a DR at the June, 2024 meeting.]
There is an inconsistency in the handling of referencesvs pointers in user defined conversions and overloading. The reasonfor that is that the combination of 9.5.4 [dcl.init.ref] and7.3.6 [conv.qual] circumvents the standard way of rankingconversion functions, which was probably not the intention of thedesigners of the standard.
Let's start with some examples, to show what it is about:
struct Z { Z(){} }; struct A { Z x; operator Z *() { return &x; } operator const Z *() { return &x; } }; struct B { Z x; operator Z &() { return x; } operator const Z &() { return x; } }; int main() { A a; Z *a1=a; const Z *a2=a; // not ambiguous B b; Z &b1=b; const Z &b2=b; // ambiguous }So while both classesA andB are structurallyequivalent, there is a difference in operator overloading. I want tostart with the discussion of the pointer case (const Z*a2=a;): 12.2.4 [over.match.best] is used to select the bestviable function. Rule 4 selectsA::operator const Z*() asbest viable function using 12.2.4.3 [over.ics.rank] since theimplicit conversion sequenceconst Z* ->const Z*is a better conversion sequence thanZ* ->constZ*.
So what is the difference to the reference case? Cv-qualificationconversion is only applicable for pointers according to 7.3.6 [conv.qual]. According to 9.5.4 [dcl.init.ref] paragraphs4-7 references are initialized by binding using the concept ofreference-compatibility. The problem with this is, that in thiscontext of binding, there is no conversion, and therefore there isalso no comparing of conversion sequences. More exactly allconversions can be considered identity conversions according to12.2.4.2.5 [over.ics.ref] paragraph 1, which compare equaland which has the same effect. So bindingconst Z* toconst Z* is as good as bindingconst Z* toZ* in terms of overloading. Thereforeconst Z&b2=b; is ambiguous. [12.2.4.2.5 [over.ics.ref] paragraph 5and 12.2.4.3 [over.ics.rank] paragraph 3 rule 3(S1 and S2 are reference bindings ...) do not seem to apply to thiscase]
There are other ambiguities, that result in the special treatmentof references: Example:
struct A {int a;}; struct B: public A { B() {}; int b;}; struct X { B x; operator A &() { return x; } operator B &() { return x; } }; main() { X x; A &g=x; // ambiguous }Since both references of classA andB arereference compatible with references of classA and sincefrom the point of ranking of implicit conversion sequences they areboth identity conversions, the initialization is ambiguous.
So why should this be a defect?
So overall I think this was not the intention of the authors of thestandard.
So how could this be fixed? For comparing conversion sequences (andonly for comparing) reference binding should be treated as if it was anormal assignment/initialization and cv-qualification would have to bedefined for references. This would affect 9.5.4 [dcl.init.ref] paragraph 6, 7.3.6 [conv.qual] and probably12.2.4.3 [over.ics.rank] paragraph 3.
Another fix could be to add a special case in 12.2.4 [over.match.best] paragraph 1.
CWG 2023-06-13
It was noted that the second example is not ambiguous, because aderived-to-base conversion is compared against an identity conversion.However, 12.2.4.2.5 [over.ics.ref] paragraph 1 needs a wording fixso that it applies to conversion functions as well. CWG opined thatthe first example be made valid, by adding a missing tie-breaker forthe conversion function case.
Proposed resolution (approved by CWG 2024-04-19):
Change in 12.2.4.1 [over.match.best.general] bullet 2.2 as follows:
- ...
- the context is an initialization by user-defined conversion (see9.5 [dcl.init], 12.2.2.6 [over.match.conv], and12.2.2.7 [over.match.ref]) and the standard conversion sequencefrom the
return typeresult of F1 to thedestination type (i.e., the type of the entity being initialized) is abetter conversion sequence than the standard conversion sequence fromthereturn typeresult of F2 to the destinationtype- ...
Add a new sub-bullet to 12.2.4.3 [over.ics.rank] bullet 3.2as follows:
- ...
- S1 and S2 include reference bindings (9.5.4 [dcl.init.ref]), andthe types to which the references refer are the same type except fortop-level cv-qualifiers, and the type to which the referenceinitialized by S2 refers is more cv-qualified than the type to whichthe reference initialized by S1 refers
.[ Example: ... -- end example ]or, if not that,- S1 andS2 bind the same reference type "reference toT" and have source typesV1 andV2,respectively, where the standard conversion sequence fromV1*toT* is better than the standard conversion sequencefromV2* toT*. [ Example:
struct Z {}; struct A { operator Z&(); operator const Z&(); //#1 }; struct B { operator Z(); operator const Z&&(); //#2 }; const Z& r1 = A(); //OK, uses #1 const Z&& r2 = B(); //OK, uses #2--- end example]