Учитывая типы A,B
, меня интересует точное определение std::common_type<A,B>
, не учитывая вариационный случай std::common_type<A...>
для произвольных типов A...
. Итак, пусть
using T = decltype(true ? std::declval<A>() : std::declval<B>());
using C = std::common_type<A,B>;
Теперь, согласно ряду источников, я нашел следующие отношения (для краткости пропуская typename
):
-
cppreference.com:
C::type = std::decay<T>::type
-
cplusplus.com:
C::type = T
-
GCC 4.8.1
<type_traits>
реализация:C::type = std::decay<T>::type
еслиT
действительна, в противном случаеC
не содержит члена::type
( "SFINAE-friendly" ) -
Clang 3.3
<type_traits>
реализация:C::type = std::remove_reference<T>::type
Я нахожу "SFINAE-friendly" версию GCC второстепенной деталью, тогда как std::remove_reference
и std::decay
практически отличаются только встроенными массивами и функциями, плюс cv-qualification, для которых опять меня не беспокоят, Поэтому мой вопрос:
Должно ли это быть decay<T>::type
или просто T
? В чем обоснование использования decay<T>::type
? Речь идет только о представлении результата A() + B()
, например. для арифметических выражений?
Например, экспериментируя немного, я обнаружил, что в случае определения "just T
" мы имеем
common_type<int&,int&> = int&
common_type<int&,long&> = long
то есть ссылка lvalue поддерживается, если типы равны. Это отражает тот факт, что
int a, b;
(true ? a : b) = 0;
а
int a;
long b;
(true ? a : b) = 0;
нет. Эта семантика "разрешения присвоения, если типы равны" - это именно то, что мне нужно в одном приложении, и я склонен полагать, что common_type
и decay
должны быть двумя независимыми шагами. Должен ли я просто использовать свои собственные определения?