Given types A,B
, I am concerned with the exact definition of std::common_type<A,B>
, disregarding the variadic case std::common_type<A...>
for arbitrary types A...
. So let
using T = decltype(true ? std::declval<A>() : std::declval<B>());
using C = std::common_type<A,B>;
Now, according to a number of sources, I have found the following relations (skipping typename
for brevity):
cppreference.com: C::type = std::decay<T>::type
cplusplus.com: C::type = T
GCC 4.8.1 <type_traits>
implementation: C::type = std::decay<T>::type
if T
is valid, otherwise C
does not contain a ::type
member ("SFINAE-friendly")
Clang 3.3 <type_traits>
implementation: C::type = std::remove_reference<T>::type
I find the "SFINAE-friendly" version of GCC a minor detail, while std::remove_reference
and std::decay
practically only differ in built-in arrays and functions, plus cv-qualification, for which again I am not concerned much. So my question is
Should it be decay<T>::type
or just T
? What is the rationale of using decay<T>::type
? Is it only about representing result A() + B()
e.g. for arithmetic expressions?
For instance, experimenting a bit, I have found that in the case of the "just T
" definition, we have
common_type<int&,int&> = int&
common_type<int&,long&> = long
that is, an lvalue reference is maintained if types are equal. This reflects the fact that
int a, b;
(true ? a : b) = 0;
is valid, while
int a;
long b;
(true ? a : b) = 0;
is not. This semantics of "allowing assignment if types are equal" is exactly what I need in one application, and I tend to believe that common_type
and decay
should be two independent steps. Should I just use my own definitions?
should std::common_type use std::decay?
Yes, see Library Working Group Defect #2141.
Short version (long version, see link above):
declval<A>()
returns a A&&
common_type
is specified via declval
, n3337:
template <class T, class U>
struct common_type<T, U> {
typedef decltype(true ? declval<T>() : declval<U>()) type;
};
common_type<int, int>::type
therefore yields int&&
, which is unexpected
proposed resolution is to add decay
template <class T, class U>
struct common_type<T, U> {
typedef decay_t < decltype(true ? declval<T>() : declval<U>()) > type;
};
common_type<int, int>::type
now yields int
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With