Учитывая типы 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 должны быть двумя независимыми шагами. Должен ли я просто использовать свои собственные определения?