Title
decay_t in the new common_type fallback should be remove_cvref_t
Status
new
Section
[meta.trans.other]
Submitter
Casey Carter

Created on 2019-05-10.00:00:00, last changed 2019-06-10.05:16:56.

Messages

Date: 2019-06-10.05:16:56

Proposed resolution:

This wording is relative to N4810.

  1. Modify [meta.trans.other] as indicated:

    -2- Let:

    1. (2.1) — CREF(A) be add_lvalue_reference_t<const remove_reference_t<A>>,

    2. (2.2) — […]

    3. […]

    4. (2.9) — […]

    If any of the types computed above is ill-formed, then COMMON_REF(A, B) is ill-formed.

    -3- Note A: For the common_type trait applied to a template parameter pack T of types, the member type shall be either defined or not present as follows:

    1. (3.1) — […]

    2. (3.2) — […]

    3. (3.3) — If sizeof...(T) is two, let the first and second types constituting T be denoted by T1 and T2, respectively, and let D1 and D2 denote the same types as decay_t<T1> and decay_t<T2>, respectively.

      1. (3.3.1) — […]

      2. (3.3.2) — […]

      3. (3.3.3) — Otherwise, if

        decay_t<decltype(false ? declval<D1>() : declval<D2>())>
        

        denotes a valid type, let C denote that type.

      4. (3.3.4) — Otherwise, if COND_RES(CREF(D1), CREF(D2))

        remove_cvref_t<decltype(false ? declval<const D1&>() : declval<const D2&>())>
        

        denotes a type, let C denote theat type decay_t<COND_RES(CREF(D1), CREF(D2))>.

    4. (3.4) — […]

    [Note: Whenever the qualified-id common_type<T...>::type is valid, it denotes the same type as decay_t<common_type<T...>::type>. — end note]

    -4- Note B: […]

Date: 2019-06-12.00:00:00

[ 2019-06-12 Priority set to 3 after reflector discussion ]

Date: 2019-05-18.17:51:30

P0898R4 "The One Ranges Proposal" added a new fallback case to the definition of common_type in [meta.trans.other], bullet 3.3.4:

Otherwise, if COND_RES(CREF(D1), CREF(D2)) denotes a type, let C denote the type decay_t<COND_RES(CREF(D1), CREF(D2))>.

Per para 3.3, D1 and D2 are decayed types. If both are void, bullet 3.3.4 is not reached. If either is an abominable function type or void, the COND_RES type expression above is ill-formed and bullet 3.3.4 does not apply. In all cases in which the COND_RES expression is well-formed, D1 and D2 denote cv-unqualified non-array object types. Given that fact, (1) CREF(D1) and CREF(D2) are equivalent to const D1& and const D2&, respectively, and (2) the COND_RES expression is equivalent to decltype(false ? declval<const D1&>() : declval<const D1&>()), i.e., the second and third operands of the conditional operator are lvalues of type const D1 and const D2, respectively.

[expr.cond]/3 cannot apply since the operands are not glvalue bit-fields.

If D1 and D2 are the same type, [expr.cond]/4 does not apply. If D1 and D2 are different types, there are a few cases to consider:

  1. If [expr.cond]/4.1 applies, one operand is converted into an lvalue reference to the type of the other, i.e., both resulting operands are lvalues of type either const D1 or const D2.

  2. [expr.cond]/4.2 cannot apply since neither operand is an xvalue.

  3. [expr.cond]/4.3.1 cannot apply since it would imply that the operands have the same type.

  4. If [expr.cond]/4.3.2 applies — if either D1 or D2 is a base class of the other — again the resulting operands are lvalues of type either const D1 or const D2.

  5. If [expr.cond]/4.3.3 applies, the either the const D1& operand converts to const D2 or the const D2& operand converts to const D1.

  6. If none of the sub-bullets in [expr.cond]/4 applies, the operands are left unchanged.

[expr.cond]/5 applies if the operands initially had the same type, or in cases 1 and 4 above. The conditional expression is an lvalue of type const D1 or const D2, and the COND_RES expression yields const D1& or const D2&.

Only cases 5 and 6 reach [expr.cond]/6. This paragraph performs overload resolution, which may result in converting both operands to the same non-class type to invoke a builtin conditional operator "overload".

[expr.cond]/7 applies standard conversions including array-to-pointer and function-to-pointer conversion to the operands. Consequently, the operands are once more "decayed" if [expr.cond]/6 converted them to an array or function type. Again case-by-case:

  1. [expr.cond]/7.1 applies if the operands now have the same type, which is the type of the conditional expression.

  2. [expr.cond]/7.2 applies if the operands have arithmetic or enumeration type; the conditional expression yields the result of applying the usual arithmetic conversions.

  3. [expr.cond]/7.3 applies if the operands have pointer type; the conditional expression yields their composite pointer type.

  4. [expr.cond]/7.4 applies if the operands have pointer-to-member type; the conditional expression applies some more standard conversions and yields their composite pointer type.

  5. [expr.cond]/7.5 applies if one operand has type nullptr_t and the other is either a null pointer constant or has type nullptr_t; the conditional expression yields nullptr_t.

In every case above, the conditional expression is either ill-formed, an lvalue of type const D1 or const D2, or a prvalue of a non-array non-function type. Consequently the COND_RES type expression always yields a non-array non-function type, for which decay_t and remove_cvref_t are equivalent. We can therefore replace COND_RES(CREF(D1), CREF(D2)) in [meta.trans.other]/3.3.4 with decltype(false ? declval<const D1&>() : declval<const D2&>()), and replace the usage of decay_t with remove_cvref_t.

Furthermore, there are now quite a few different cases describing the behavior of common_type. It's not clear that common_type<T...>::type is always a decayed type without in-depth analysis. We should non-normatively clarify that fact.

History
Date User Action Args
2019-06-10 05:16:56adminsetmessages: + msg10435
2019-05-18 17:51:30adminsetmessages: + msg10407
2019-05-18 17:51:30adminsetmessages: + msg10406
2019-05-18 17:51:30adminrestoredNone
2019-05-16 18:00:29adminretiredNone
2019-05-16 18:00:29adminsetmessages: - msg10404
2019-05-10 00:00:00admincreate
Note:
This event is not handled by the history display!