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.
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, letC
denote 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
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.
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 const D1&
or const D2&
.
[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 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
.
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) —CREF(A)
beadd_lvalue_reference_t<const remove_reference_t<A>>
,(2.2) — […]
[…]
(2.9) — […]
If any of the types computed above is ill-formed, then
-3- Note A: For theCOMMON-REF(A, B)
is ill-formed.common_type
trait applied to a template parameter packT
of types, the membertype
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 constitutingT
be denoted byT1
andT2
, respectively, and letD1
andD2
denote the same types asdecay_t<T1>
anddecay_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 theat type.decay_t<COND-RES(CREF(D1), CREF(D2))>
(3.4) — […]
[Note: Whenever the qualified-id
-4- Note B: […]common_type<T...>::type
is valid, it denotes the same type asdecay_t<common_type<T...>::type>
. — end note]