Должен ли std:: common_type использовать std:: decay?

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

Ответ 1

должен ли std:: common_type использовать std:: decay?

Да, см. Дефект рабочей группы библиотеки # 2141.

Краткая версия (длинная версия, см. ссылку выше):

  • declval<A>() возвращает a A&&

  • common_type указан через 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 поэтому дает int&&, что является неожиданным

  • предлагаемая резолюция заключается в добавлении 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 теперь дает int