Title
Deleted conversions in conditional operator operands
Status
cd4
Section
8.16 [expr.cond]
Submitter
Richard Smith

Created on 2014-03-17.00:00:00, last changed 2017-02-06.00:00:00.

Messages

Date: 2015-10-15.00:00:00

Proposed resolution (October, 2015):

  1. Add the following as a new paragraph following 8.16 [expr.cond] paragraph 2:

  2. Otherwise, if the second and third operand are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2.
  3. Change 8.16 [expr.cond] paragraph 3 as follows:

  4. Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert form an implicit conversion sequence (16.3.3.1 [over.best.ics]) from each of those operands to the type of the other. [Note: Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. —end note] The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows: Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows:

    • If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (Clause 7 [conv]) to the type, the target type is “lvalue reference to T2”, subject to the constraint that in the conversion the reference must bind directly (11.6.3 [dcl.init.ref]) to an lvalue.

    • If E2 is an xvalue: E1 can be converted to match E2 if E1 can be implicitly converted to the type, the target type is “rvalue reference to T2”, subject to the constraint that the reference must bind directly.

    • If E2 is a prvalue or if neither of the conversions conversion sequences above can be done formed and at least one of the operands has (possibly cv-qualified) class type:

      • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to a prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand. if T1 and T2 are the same class type (ignoring cv-qualification), or one is a base class of the other, and T2 is at least as cv-qualified as T1, the target type is T2,

      • Otherwise (if E1 or E2 has a non-class type, or if they both have class types but the underlying classes are not the same and neither is a base class of the other): E1 can be converted to match E2 if E1 can be implicitly converted to otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2 [conv.array]), and function-to-pointer (7.3 [conv.func]) standard conversions.

    Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand can be converted to match to the target type determined for the third operand, and whether the third operand can be converted to match the second operand vice versa. If both can be converted sequences can be formed, or one can be converted but the conversion is formed, but it is the ambiguous conversion sequence, the program is ill-formed. If neither can be converted no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section. [Note: The conversion might be ill-formed even if an implicit conversion sequence could be formed. —end note]

This resolution also resolves issue 1932.

Date: 2014-06-15.00:00:00

Notes from the June, 2014 meeting:

The wording should be changed to handle the convertibility test more like overload resolution: the conversion "exists" if the conversion function is declared, but is ill-formed if it would actually be used.

Date: 2016-02-15.00:00:00

[Adopted at the February, 2016 meeting.]

In an example like,

  struct B;
  struct A { A(); A(B&) = delete; operator B&(); };
  struct B : A {} b;
  B &c = true ? A() : b;

the rules of 8.16 [expr.cond] paragraph 3 make this ambiguous: A() can be implicitly converted to the type “lvalue reference to B,” and b satisfies the constraints to be converted to an A prvalue (it's of a type derived from A and the cv-qualifiers are okay). Bullet 3 bullet 1 is clear that we do not actually try to create an A temporary from b, so we don't notice that it invokes a deleted constructor and rule out that conversion.

If the deleted conversion is in the other sense, the result is unambiguous:

  struct B;
  struct A { A(); A(B&); operator B&() = delete; };
  struct B : A {} b;
  B &c = true ? A() : b;

A() can no longer be implicitly converted to the type “lvalue reference to B”: since the declaration B &t = A(); is not well formed (it invokes a deleted function), there is no implicit conversion. So we unambiguously convert the third operand to an A prvalue.

These should presumably either both be valid or both invalid. EDG and gcc call both ambiguous.

History
Date User Action Args
2017-02-06 00:00:00adminsetstatus: ready -> cd4
2015-11-10 00:00:00adminsetmessages: + msg5571
2015-11-10 00:00:00adminsetstatus: drafting -> ready
2014-07-07 00:00:00adminsetmessages: + msg5088
2014-07-07 00:00:00adminsetstatus: open -> drafting
2014-03-17 00:00:00admincreate