Подтвердить что ты не робот

Почему "declval" указано в терминах "add_rvalue_reference <T>:: type", а не "T &&"?

§20.2.4 [declval]

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

Зачем использовать add_rvalue_reference здесь?

От §20.9.7.2 [meta.trans.ref] от add_rvalue_reference:

Если T называет объект или тип функции, то член typedef type должен называть T&&; в противном случае type называет T. [Примечание. Это правило отражает семантику ссылочного коллапса (8.3.2). Например, когда тип T называет тип T1&, тип add_rvalue_reference<T>::type не является ссылкой rvalue. -end note]

Так как add_rvalue_reference предназначен для отражения ссылки в любом случае, почему бы просто не использовать T&&, как показано ниже?

template<class T>
T&& declval();

Что может пойти не так? Каковы различия между этими двумя версиями?

4b9b3361

Ответ 1

Я не знаю, является ли это реальной причиной, но add_rvalue_reference имеет другое поведение для void.

add_rvalue_reference<void>::type просто void.

void&& - ошибка.

Ответ 2

Несколько определений зависят от declval, дающего разумные результаты для cv-qual void. Пример: is_assignable:

template <class T, class U>
struct is_assignable;

Выражение declval<T>() = declval<U>() хорошо сформировано при обработке как неоцениваемый операнд...

Цель состоит в том, что "хорошо сформированный" относится к хорошо сформированному выражению присваивания, а не к самому правилу declval<T>. То есть мы хотим беспокоиться только об одной вещи за раз.

Ответ 3

Разница в том, что add_rvalue_reference<> только добавляет часть &&, если T - это объект или тип функции. Если T не является объектом или типом функции (например, void), вы не хотите добавлять &&.

См. этот пример в Ideone.
Эта веб-страница внедрения Boost объясняет:

Роль шаблона функции declval() представляет собой преобразование типа T в значение без использования или оценки этой функции. Предполагается, что это имя направит внимание читателя на то, что выражение declval<T>() является lvalue тогда и только тогда, когда T является ссылкой lvalue, иначе rvalue. Чтобы расширить область этой функции, мы можем сделать немного лучше, изменив ее объявление на

template<class T>
typename std::add_rvalue_reference<T>::type declval(); // not used

который гарантирует, что мы также можем использовать cv void в качестве параметра шаблона.