should std::common_type use std::decay?
c++, c++11, conditional-operator, lvalue, type-traits
Solution
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`
Problem
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?