Почему std:: declval добавляет ссылку?
std::declval
- утилита времени компиляции, используемая для построения выражения с целью определения его типа. Он определяется следующим образом:
template< class T >
typename std::add_rvalue_reference<T>::type declval() noexcept;
Не проще ли это?
template< class T >
T declval() noexcept;
В чем преимущество ссылочного типа возврата? И не следует ли это называть declref
?
Самый ранний исторический пример, который я нахожу, n2958, который вызывает функцию value()
, но уже всегда возвращает ссылку.
Обратите внимание, что операнд decltype
не должен иметь доступный деструктор, т.е. он не семантически проверяется как полное выражение.
template< typename t >
t declprval() noexcept;
class c { ~ c (); };
decltype ( declprval< c >() ) * p = nullptr; // OK
Ответы
Ответ 1
"Нет временного введения для возврата функции prvalue типа объекта в decltype
" правило применяется только в том случае, если сам вызов функции является либо операндом decltype
, либо правым операндом оператора запятой, что операнд decltype
(§5.2.2 [expr.call]/p11), что означает, что данный declprval
в OP,
template< typename t >
t declprval() noexcept;
class c { ~ c (); };
int f(c &&);
decltype(f(declprval<c>())) i; // error: inaccessible destructor
не компилируется. В более общем плане возврат T
предотвратит большинство нетривиальных применений declval
с неполными типами, тип с частными деструкторами и т.п.:
class D;
int f(D &&);
decltype(f(declprval<D>())) i2; // doesn't compile. D must be a complete type
и это не имеет особого преимущества, поскольку значения x почти не отличаются от prvalues, за исключением случаев, когда вы используете decltype
на них, и обычно вы не используете decltype
непосредственно для возвращаемого значения declval
- вы знаете, тип уже.
Ответ 2
Массивы не могут быть возвращены значением, поэтому даже объявление функции, возвращающей массив по значению, является недопустимым кодом.
Однако вы можете вернуть массив по ссылке.
Ответ 3
Цель decltype()
состоит в том, чтобы иметь выражение, которое действует как допустимое значение типа T
, чтобы выразить его как T
в выражениях, ожидающих T
s. Проблема в том, что в С++ тип T
может быть не скопированным или даже нестандартным по построению. Поэтому использование T{}
для этой цели не работает.
То, что decltype()
заключается в возврате ссылки rvalue на T
. Ссылка rvalue должна быть действительна для любого типа T
, поэтому она гарантирует, что у нас есть действительный T
из ссылки rvalue T
, и, как он гарантирует, мы можем иметь ссылку rvalue для любого типа T
. Это трюк.
Подумайте о decltype()
как "дайте мне действительное выражение типа T
". Конечно, его использование предназначено для разрешения перегрузки, определения типа и т.д.; так как его цель - вернуть правильное выражение (в синтаксическом смысле), чтобы не возвращать значение. То, что отражено в том, что std::declval()
вообще не определено, его только объявлено.
Если он был определен, мы снова имеем исходную проблему (мы должны построить значение для произвольного типа T
, и это невозможно).