This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++14 status.
common_type trait produces reference typesSection: 21.3.9.7[meta.trans.other]Status:C++14Submitter: Doug GregorOpened: 2012-03-11Last modified: 2016-01-28
Priority:Not Prioritized
View all otherissues in [meta.trans.other].
View all issues withC++14 status.
Discussion:
The type computation of thecommon_type type trait is defined as
template <class T, class U> struct common_type<T, U> { typedef decltype(true ? declval<T>() : declval<U>()) type; };This means thatcommon_type<int, int>::type isint&&, because
declval<int>() returnsint&&decltype returnsT&& when its expression is an xvalue (9.2.9.3[dcl.type.simple] p4)Users ofcommon_type do not expect to get a reference type as the result; the expectation is thatcommon_type will return a non-reference type to which all of the types can be converted.
std::unique_ptr'soperator< in 20.3.1.6[unique.ptr.special] (around p4) is also broken: In the most typical case (with default deleter), the determination of the common pointer typeCT will instantiatestd::less<CT> which can now bestd::less<T*&&>, which willnot be the specialization of pointer types that guarantess a total order.Given the historic constext ofcommon_type original specification, the proper resolution to meseems to be usingstd::decay instead ofstd::remove_reference:template <class T, class U>struct common_type<T, U> { typedeftypename decay<decltype(true ? declval<T>() : declval<U>())>::type type;};At that time rvalues had no identity in this construct and rvalues of non-class types have no cv-qualification.With this change we would ensure that
common_type<int, int>::type == common_type<const int, const int>::type == int
Note that this harmonizes with the corresponding heterogenous case, which has already the exact same effect:
common_type<int, long>::type == common_type<const int, const long>::type == long
[2012-10-11 Daniel comments]
While testing the effects of applying the proposed resolution I noticed that this will have the effect that the unary form ofcommon_type, like
common_type<int>
is not symmetric to the n-ary form (n > 1). This is unfortunate, because this difference comes especially to effect whencommon_type is used with variadic templates. As an example consider the followingmake_array template:
#include <array>#include <type_traits>#include <utility>template<class... Args>std::array<typename std::common_type<Args...>::type, sizeof...(Args)>make_array(Args&&... args){ typedef typename std::common_type<Args...>::type CT; return std::array<CT, sizeof...(Args)>{static_cast<CT>(std::forward<Args>(args))...};}int main(){ auto a1 = make_array(0); // OK: std::array<int, 1> auto a2 = make_array(0, 1.2); // OK: std::array<double, 2> auto a3 = make_array(5, true, 3.1415f, 'c'); // OK: std::array<float, 4> int i = 0; auto a1b = make_array(i); //Error, attempt to form std::array<int&, 1> auto a2b = make_array(i, 1.2); // OK: std::array<double, 2> auto a2c = make_array(i, 0); // OK: std::array<int, 2>}The error fora1bonly happens in the unary case and it is easy that it remains unnoticedduring tests. You cannot explain that reasonably to the user here.
std::decay to the result of thestd::common_type deduction. But if this is necessary here, I wonder why it should also be applied to the binary case, where it gives the wrong illusion of a complete type decay? The other way around: Why isstd::decay not also applied to the unary case as well?This problem is not completely new and was already observed for the originalstd::common_type specification. At this time thedecltype rules had a similar asymmetric effect when comparing
std::common_type<const int, const int>::type(equal to 'int' at this time)
with:
std::common_type<const int>::type(equal to 'const int')
and I wondered whether the unary form shouldn't also perform the same "decay" as the n-ary form.
This problem makes me think that the current resolution proposal might not be ideal and I expectdifferences in implementations (for those who consider to apply this proposed resolution already). Isee at least three reasonable options:Accept the current wording suggestion for LWG 2141 as it is and explain that to users.
Keepstd::common_type as currently specified in the Standard and tell users to usestd::decay where needed. Also fix other places in the library, e.g. the comparisonfunctions ofstd::unique_ptr or a most of the time library functions.
Applystd::decay also in the unary specialization ofstd::common_type withthe effect thatstd::common_type<const int&>::type returnsint.
[2012-10-11 Marc Glisse comments]
If we are going with decay everywhere, I wonder whether we should also decay in the 2-argument versionbefore and not onlyafter. So if I specializecommon_type<mytype, double>,common_type<const mytype, volatile double&> would automatically work.
[2012-10-11 Daniel provides wording for bullet 3 of his list:]
Change 21.3.9.7[meta.trans.other] p3 as indicated:
template <class T>struct common_type<T> { typedeftypename decay<T>::type type;};template <class T, class U>struct common_type<T, U> { typedeftypename decay<decltype(true ? declval<T>() : declval<U>())>::type type;};[2013-03-15 Issues Teleconference]
Moved to Review.
Want to carefully consider the effect ofdecay vs.remove_reference with respectto constness before adopting, although this proposed resolution stands for review in Bristol.
[2013-04-18, Bristol meeting]
Previous wording:
This wording is relative to N3376.
In 21.3.9.7[meta.trans.other] p3, change the
common_typedefinition totemplate <class T, class U>struct common_type<T, U> { typedeftypename decay<decltype(true ? declval<T>() : declval<U>())>::type type;};
[2013-04-18, Bristol]
Move to Ready
[2013-09-29, Chicago]
Accepted for the working paper
Proposed resolution:
This wording is relative to N3485.
Change 21.3.9.7[meta.trans.other] p3 as indicated:
template <class T>struct common_type<T> { typedeftypename decay<T>::type type;};template <class T, class U>struct common_type<T, U> { typedeftypename decay<decltype(true ? declval<T>() : declval<U>())>::type type;};