This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.
Section: 21.3.8.7 [meta.trans.other] Status: Resolved Submitter: Eric Niebler Opened: 2015-01-12 Last modified: 2017-09-07
Priority: 2
View all other issues in [meta.trans.other].
View all issues with Resolved status.
Discussion:
I think there's a defect regarding common_type and its specializations. Unless I've missed it, there is nothing preventing folks from instantiating common_type with cv-qualified types or reference types. In fact, the wording in N3797 explicitly mentions cv void, so presumably at least cv qualifications are allowed.
Users are given license to specialize common_type when at least of of the types is user-defined. (A separate issue is the meaning of user-defined. In core, I believe this is any class/struct/union/enum, but in lib, I think it means any type not defined in std, right?) There is at least one place in the standard that specializes common_type (time.traits.specializations) on time_point and duration. But the specializations are only for non-cv-qualified and non-reference specializations of time_point and duration. If the user uses, say, common_type<duration<X,Y> const, duration<A,B> const>, they're not going to get the behavior they expect. Suggest we clarify the requirements of common_type's template parameters. Also, perhaps we can add blanket wording that common_type<A [cv][&], B [cv][&]> is required to be equivalent to common_type<A,B> (if that is in fact the way we intent this to work). Also, the change to make common_type SFINAE-friendly regressed key functionality, as noted by Agustín K-ballo Bergé in c++std-lib-37178. Since decay_t is not applied until the very end of the type computation, user specializations are very likely to to be found. Agustín says:Consider the following snippet:
struct X {}; struct Y { explicit Y(X){} }; namespace std { template<> struct common_type<X, Y> { typedef Y type; }; template<> struct common_type<Y, X> { typedef Y type; }; } static_assert(is_same<common_type_t<X, Y>, Y>()); // (A) static_assert(is_same<common_type_t<X, Y, Y>, Y>()); // (B) static_assert(is_same<common_type_t<X, X, Y>, Y>()); // (C)Under the original wording, all three assertion holds. Under the current wording,
(A) picks the user-defined specialization, so the assertion holds.
(B) goes to the third bullet and, ignoring the user-defined specialization, looks for decltype(true ? declval<X>() : declval<Y>()); since it is ill-formed there is no common type.
(C) goes to the third bullet and yields common_type_t<X&&, Y>, which again misses the user-defined specialization.
The discussion following c++std-lib-35636 seemed to cohere around the idea that the primary common_type specialization should have the effect of stripping top-level ref and cv qualifiers by applying std::decay_t to its arguments and, if any of them change as a result of that transformation, re-dispatching to common_type on those transformed arguments, thereby picking up any user-defined specializations. This change to common_type would make the specializations in time.traits.specializations sufficient.
Suggested wording: I'm afraid I don't know enough to suggest wording. But for exposition, the following is my best shot at implementing the suggested resolution. I believe it also fixes the regression noted by Agustín K-ballo Bergé in c++std-lib-37178.namespace detail { template<typename T, typename U> using default_common_t = decltype(true? std::declval<T>() : std::declval<U>()); template<typename T, typename U, typename Enable = void> struct common_type_if {}; template<typename T, typename U> struct common_type_if<T, U, void_t<default_common_t<T, U>>> { using type = decay_t<default_common_t<T, U>>; }; template<typename T, typename U, typename TT = decay_t<T>, typename UU = decay_t<U>> struct common_type2 : common_type<TT, UU> // Recurse to catch user specializations {}; template<typename T, typename U> struct common_type2<T, U, T, U> : common_type_if<T, U> {}; template<typename Meta, typename Enable = void> struct has_type : std::false_type {}; template<typename Meta> struct has_type<Meta, void_t<typename Meta::type>> : std::true_type {}; template<typename Meta, typename...Ts> struct common_type_recurse : common_type<typename Meta::type, Ts...> {}; template<typename Meta, typename...Ts> struct common_type_recurse_if : std::conditional< has_type<Meta>::value, common_type_recurse<Meta, Ts...>, empty >::type {}; } template<typename ...Ts> struct common_type {}; template<typename T> struct common_type<T> { using type = std::decay_t<T>; }; template<typename T, typename U> struct common_type<T, U> : detail::common_type2<T, U> {}; template<typename T, typename U, typename... Vs> struct common_type<T, U, Vs...> : detail::common_type_recurse_if<common_type<T, U>, Vs...> {};
[2016-08 Chicago]
Walter and Nevin provide wording.
Previous resolution [SUPERSEDED]:
[This also resolves the first part of 2460]
In Table 46 of N4604, entry for common_type:
... may specialize this trait if at least one template parameter in the specialization is a user-defined type and no template parameter is cv-qualified.
In [meta.trans.other] bullet 3.3:
... whose second operand is an xvalue of type
T1decay_t<T1>, and whose third operand is an xvalue of typeT2decay_t<T2>. If ...
[2016-08-02, Chicago: Walt, Nevin, Rob, and Hal provide revised wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4606.
[This also resolves the first part of LWG 2460]
In Table 46 — "Other transformations" edit the entry for common_type:
Table 46 — Other transformations Template Comments … template <class... T>
struct common_type;The member typedef type shall be defined or omitted as specified below.
If it is omitted, there shall be no member type. All types in the
parameter pack T shall be complete or (possibly cv) void.
A program may specialize this trait for two cv-unqualified non-reference types
if at least onetemplate parameter in the specializationof them
is a user-defined type. [Note: Such specializations are
needed when only explicit conversions are desired among the template
arguments. — end note]… Edit 21.3.8.7 [meta.trans.other] p3 (and its subbullets) as shown below
For the common_type trait applied to a parameter pack T of types, the member type shall be either defined or not present as follows:
If sizeof...(T) is zero, there shall be no member type.
If sizeof...(T) is one, let T0 denote the sole type in the pack T. The member typedef type shall denote the same type as decay_t<T0>.
If sizeof...(T) is two, let
T1
andT2
, respectively, denote the first and second types comprisingT
, and letD1
andD2
, respectively, denotedecay_t<T1>
anddecay_t<T2>
.
If
is_same_v<T1, D1>
andis_same_v<T2, D2>
, and if there is no specializationcommon_type<T1, T2>
, letC
denote the type, if any, of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of typebool
, whose second operand is an xvalue of typeD1
, and whose third operand is an xvalue of typeD2
. If there is such a typeC
, the member typedeftype
shall denoteC
. Otherwise, there shall be no membertype
.If
not is_same_v<T1, D1>
ornot is_same_v<T2, D2>
, the member typedeftype
shall denote the same type, if any, as common_type_t<D1, D2>. Otherwise, there shall be no membertype
.If sizeof...(T) is greater than
onetwo, let T1, T2, and R, respectively, denote the first, second, and (pack of) remaining types comprising T.[Note: sizeof...(R) may be zero. — end note] Let C denote the type, if any, of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of type bool, whose second operand is an xvalue of type T1, and whose third operand is an xvalue of type T2.LetC
denotecommon_type_t<T1, T2>
. If there is such a type C, the member typedef type shall denote the same type, if any, as common_type_t<C, R...>. Otherwise, there shall be no member type.
[2016-08-03 Chicago LWG]
LWG asks for minor wording tweaks and for an added Note. Walter revises the Proposed Resolution accordingly.
Previous resolution [SUPERSEDED]:
This wording is relative to N4606.
[This also resolves the first part of LWG 2460]
In Table 46 — "Other transformations" edit the entry for common_type:
Table 46 — Other transformations Template Comments … template <class... T>
struct common_type;The member typedef type shall be defined or omitted as specified below.
If it is omitted, there shall be no member type. All types in the
parameter pack T shall be complete or (possibly cv) void.
A program may specialize this trait for two cv-unqualified non-reference types
if at least onetemplate parameter in the specializationof them
is a user-defined type. [Note: Such specializations are
needed when only explicit conversions are desired among the template
arguments. — end note]… Edit 21.3.8.7 [meta.trans.other] p3 (and its subbullets) as shown below
For the common_type trait applied to a parameter pack T of types, the member type shall be either defined or not present as follows:
(3.1) — If sizeof...(T) is zero, there shall be no member type.
(3.2) — If sizeof...(T) is one, let T0 denote the sole type in the pack T. The member typedef type shall denote the same type as decay_t<T0>.
(3.3) — If sizeof...(T) is two, let
T1
andT2
, respectively, denote the first and second types comprisingT
, and letD1
andD2
, respectively, denotedecay_t<T1>
anddecay_t<T2>
.
(3.3.1) — If
is_same_v<T1, D1>
andis_same_v<T2, D2>
, letC
denote the type of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of typebool
, whose second operand is an xvalue of typeD1
, and whose third operand is an xvalue of typeD2
. [Note: This will not apply if there is a specializationcommon_type<D1, D2>
. — end note](3.3.2) — Otherwise, let
C
denote the typecommon_type_t<D1, D2>
.In either case, if there is such a type
C
, the member typedeftype
shall denoteC
. Otherwise, there shall be no membertype
.(3.4) — If sizeof...(T) is greater than
onetwo, let T1, T2, and R, respectively, denote the first, second, and (pack of) remaining types comprising T.[Note: sizeof...(R) may be zero. — end note] Let C denote the type, if any, of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of type bool, whose second operand is an xvalue of type T1, and whose third operand is an xvalue of type T2.LetC
denotecommon_type_t<T1, T2>
. If there is such a type C, the member typedef type shall denote the same type, if any, as common_type_t<C, R...>. Otherwise, there shall be no member type.
[2016-08-04 Chicago LWG]
Alisdair notes that 16.4.5.2.1 [namespace.std] p.1 seems to prohibit some kinds of specializations that we want to permit here and asks that the Table entry be augmented so as to specify the precise rules that a specialization is required to obey. Walter revises Proposed Resolution accordingly.
[2016-08-03 Chicago]
Fri PM: Move to Tentatively Ready
[2016-08-11 Daniel comments]
LWG 2763 presumably provides a superiour resolution that also fixes another bug in the Standard.
[2016-08-12]
Howard request to reopen this issue because of the problem pointed out by LWG 2763.
[2016-08-13 Tim Song comments]
In addition to the issue pointed out in LWG 2763, the current P/R no longer decays the type
of the conditional expression. However, that seems harmless since 7 [expr]/5 means that the
"type of an expression" is never a reference type, and 7.6.16 [expr.cond]'s rules appear to ensure that
the type of the conditional expression will never be "decay-able" when fed with two xvalues of cv-unqualified
non-array object type. Nonetheless, a note along the lines of "[Note: C
is never a reference,
function, array, or cv-qualified type. — end note]" may be appropriate, similar to the note
at the end of [dcl.decomp]/1.
[2016-11-12, Issaquah]
Resolved by P0435R1
Proposed resolution:
This wording is relative to N4606.
[This also resolves the first part of LWG 2460]
In Table 46 — "Other transformations" edit the entry for common_type:
Table 46 — Other transformations Template Comments … template <class... T>
struct common_type;Unless this trait is specialized (as specified in Note B, below), t The
member typedef type shall be defined or omitted as specified in Note A, below.
If it is omitted, there shall be no member type. All types in the
parameter pack T shall be complete or (possibly cv) void.
A program may specialize this trait
if at least one template parameter in the specialization
is a user-defined type. [Note: Such specializations are
needed when only explicit conversions are desired among the template
arguments. — end note]…
Edit 21.3.8.7 [meta.trans.other] p3 (and its subbullets) as shown below
-3- Note A: For the common_type trait applied to a parameter pack T of types, the member type shall be either defined or not present as follows:
(3.1) — If sizeof...(T) is zero, there shall be no member type.
(3.2) — If sizeof...(T) is one, let T0 denote the sole type in the pack T. The member typedef type shall denote the same type as decay_t<T0>.
(3.3) — If sizeof...(T) is two, let
T1
andT2
, respectively, denote the first and second types comprisingT
, and letD1
andD2
, respectively, denotedecay_t<T1>
anddecay_t<T2>
.
(3.3.1) — If
is_same_v<T1, D1>
andis_same_v<T2, D2>
, letC
denote the type of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of typebool
, whose second operand is an xvalue of typeD1
, and whose third operand is an xvalue of typeD2
. [Note: This will not apply if there is a specializationcommon_type<D1, D2>
. — end note](3.3.2) — Otherwise, let
C
denote the typecommon_type_t<D1, D2>
.In either case, if there is such a type
C
, the member typedeftype
shall denoteC
. Otherwise, there shall be no membertype
.(3.4) — If sizeof...(T) is greater than
onetwo, let T1, T2, and R, respectively, denote the first, second, and (pack of) remaining types comprising T.[Note: sizeof...(R) may be zero. — end note] Let C denote the type, if any, of an unevaluated conditional expression (7.6.16 [expr.cond]) whose first operand is an arbitrary value of type bool, whose second operand is an xvalue of type T1, and whose third operand is an xvalue of type T2.LetC
denotecommon_type_t<T1, T2>
. If there is such a type C, the member typedef type shall denote the same type, if any, as common_type_t<C, R...>. Otherwise, there shall be no member type.-?- Note B: A program may specialize the
-4- [Example: Given these definitions: […]common_type
trait for two cv-unqualified non-reference types if at least one of them is a user-defined type. [Note: Such specializations are needed when only explicit conversions are desired among the template arguments. — end note] Such a specialization need not have a member namedtype
, but if it does, that member shall be a typedef-name for a cv-unqualified non-reference type that need not otherwise meet the specification set forth in Note A, above.