Title
Overload resolution and conversion-to-same-type operators
Status
cd2
Section
12.2.3 [over.match.viable]
Submitter
Nathan Sidwell

Created on 2007-08-02.00:00:00 last changed 143 months ago

Messages

Date: 2009-03-15.00:00:00

[Voted into the WP at the March, 2009 meeting.]

Date: 2008-03-15.00:00:00

Proposed resolution (March, 2008):

  1. Change the footnote in 11.4.8.3 [class.conv.fct] paragraph 1 as described in the October, 2007 proposed resolution.

  2. Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

  3. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    • If the initializer expression

      • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or

      • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3” [Footnote: This requires a conversion function (11.4.8.3 [class.conv.fct]) returning a reference type. —end footnote] (this conversion is selected by enumerating the applicable conversion functions (12.2.2.7 [over.match.ref]) and choosing the best one through overload resolution (12.2 [over.match])),

      then...

    [Drafting note: this resolution makes the example in the issue description ill-formed.]

Date: 2008-03-15.00:00:00

Additional note (March, 2008):

A slight change to the example above indicates that there is a need for a normative change as well as the clarification of the rationale in the October, 2007 proposed resolution. If the declaration of foo were changed to

    void foo(const xyz&);

with the current wording, the call foo(xyz()) would be interpreted as foo(xyz().operator abc&()) instead of binding the parameter directly to the rvalue, which is clearly wrong.

Date: 2007-10-15.00:00:00

Proposed Resolution (October, 2007):

Change the footnote in 11.4.8.3 [class.conv.fct] paragraph 1 as follows:

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void. [Footnote: These conversions are considered as standard conversions for the purposes of overload resolution (12.2.4.2 [over.best.ics], 12.2.4.2.5 [over.ics.ref]) and therefore initialization (9.4 [dcl.init]) and explicit casts (7.6.1.9 [expr.static.cast]). A conversion to void does not invoke any conversion function (7.6.1.9 [expr.static.cast]). Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class —end footnote]
Date: 2007-10-15.00:00:00

Notes from the October, 2007 meeting:

The intent of 11.4.8.3 [class.conv.fct] paragraph 1 is that overload resolution not be attempted at all for the listed cases; that is, if the target type is void, the object's type, or a base of the object's type, the conversion is done directly without considering any conversion functions. Consequently, the questions about whether the conversion function is part of the overload set or not are moot. The wording will be changed to make this clearer.

Date: 2007-08-02.00:00:00

11.4.8.3 [class.conv.fct] paragraph 1 says,

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.

At what point is this enforced, and how is it enforced?

  1. Does such a user-declared conversion operator participate in overload resolution? Or is it never entered into the overload set?
  2. If it does participate in overload resolution, what happens if it is selected? Is the program ill-formed (and diagnostic required), or is it silently ignored? The above wording doesn't really make it clear.

Consider this test case:

    struct abc;

    struct xyz {
       xyz();

       xyz(xyz &);

       operator xyz& (); // #1
       operator abc& (); // #2
    };

    struct abc : xyz {};

    void foo(xyz &);

    void bar() {
             foo (xyz ());
    }

If such conversion functions are part of the overload set, #1 is a better conversion than #2 to convert the temporary xyz object to a non-const reference required for foo's operand. If such conversion functions are not part of the overload set, then #2 would be selected, and AFAICT the program would be well formed.

If the conversion functions are not part of the overload set, then it would seem one cannot take their address. For instance, adding the following line to the above test case would find no suitable function:

    xyz &(xyz::*ptr) () = &xyz::operator xyz &;
History
Date User Action Args
2010-03-29 00:00:00adminsetstatus: wp -> cd2
2009-08-03 00:00:00adminsetstatus: dr -> wp
2009-03-23 00:00:00adminsetmessages: + msg2029
2009-03-23 00:00:00adminsetstatus: ready -> dr
2008-10-05 00:00:00adminsetstatus: review -> ready
2008-05-18 00:00:00adminsetmessages: + msg1639
2008-05-18 00:00:00adminsetmessages: + msg1638
2008-02-03 00:00:00adminsetmessages: + msg1568
2008-02-03 00:00:00adminsetstatus: drafting -> review
2007-10-09 00:00:00adminsetmessages: + msg1544
2007-10-09 00:00:00adminsetstatus: open -> drafting
2007-08-02 00:00:00admincreate