*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.*

**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

denotes a type, letCOND_RES(CREF(D1),CREF(D2))Cdenote the typedecay_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

[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:

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`.[expr.cond]/4.2 cannot apply since neither operand is an xvalue.

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

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`.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`.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

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:

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

[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.

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

[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.

[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

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.

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

-2- Let:

~~(2.1) —~~be*CREF*(A)`add_lvalue_reference_t<const remove_reference_t<A>>`,(2.2) — […]

[…]

(2.9) — […]

If any of the types computed above is ill-formed, then

is ill-formed.*COMMON-REF*(A, B)-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:(3.1) — […]

(3.2) — […]

(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.(3.3.1) — […]

(3.3.2) — […]

(3.3.3) — Otherwise, if

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

denotes a valid type, let

`C`denote that type.(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 th~~e~~at type.`decay_t<`*COND-RES*(*CREF*(D1),*CREF*(D2))>

(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: […]