Ответ 1
Я не знаю, является ли это реальной причиной, но add_rvalue_reference
имеет другое поведение для void
.
add_rvalue_reference<void>::type
просто void
.
void&&
- ошибка.
§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
называет объект или тип функции, то член typedeftype
должен называть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();
Что может пойти не так? Каковы различия между этими двумя версиями?
Я не знаю, является ли это реальной причиной, но add_rvalue_reference
имеет другое поведение для void
.
add_rvalue_reference<void>::type
просто void
.
void&&
- ошибка.
Несколько определений зависят от declval
, дающего разумные результаты для cv-qual void
. Пример: is_assignable
:
template <class T, class U>
struct is_assignable;
Выражение
declval<T>() = declval<U>()
хорошо сформировано при обработке как неоцениваемый операнд...
Цель состоит в том, что "хорошо сформированный" относится к хорошо сформированному выражению присваивания, а не к самому правилу declval<T>
. То есть мы хотим беспокоиться только об одной вещи за раз.
Разница в том, что 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
в качестве параметра шаблона.