This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3205. `decay_t` in the new `common_type` fallback should be `remove_cvref_t`

Section: 21.3.8.7 [meta.trans.other] Status: New Submitter: Casey Carter Opened: 2019-05-12 Last modified: 2022-04-25

Priority: 3

View all other issues in [meta.trans.other].

View all issues with New status.

Discussion:

P0898R4 "The One Ranges Proposal" added a new fallback case to the definition of `common_type` in 21.3.8.7 [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.

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

[2020-05-01; Daniel adjusts wording to recent working draft]

[2022-04-25; Daniel adjusts wording to recent working draft]

Proposed resolution:

This wording is relative to N4910.

1. Modify 21.3.8.7 [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 the at 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: […]