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

Является ли std:: declval <void>() допустимым выражением?

Насколько я знаю, я не могу объявить ссылку rvalue на void.
Например, следующий код плохо сформирован:

 void f(void &&v) { }

Из [20.2.6/1] (function template declval) у нас есть объявление для declval, которое:

template <class T>
add_rvalue_reference_t<T>
declval() noexcept;

Таким образом, declval<void> (скажем так) приведет к void &&, что я догадался, что он был плохо сформирован, как и в предыдущем примере.

В любом случае, следующий минимальный рабочий пример компилирует:

#include<utility>

int main() {
    decltype(std::declval<void>())* ptr = nullptr;
}

Обратите внимание, что также верно следующее:

static_assert(std::is_same<decltype(std::declval<void>()), void>::value, "!");

Я бы предположил, что это будет void&&, как упоминалось ранее (или, лучше, я ожидал, что он не скомпилируется).
На самом деле, это, оказывается, ссылка rvalue для любого другого не ссылочного типа.
В качестве примера:

static_assert(std::is_same<decltype(std::declval<int>()), int&&>::value, "!");

Является ли declval<void> допустимым выражением или нет? Является ли код выше законным?
Почему поведение в случае void отличается от любого другого типа? (Ибо он не работал бы иначе, может быть ответом, если код является законным).

Если это законно, то где это допускает стандарт? Я не смог найти дело.
Конечно, в стандарте говорится:

Параметр шаблона T declval может быть неполным.

В любом случае, это приведет к неприемлемому типу (void&&), и он работает вокруг него, отбрасывая ссылку rvalue.

4b9b3361

Ответ 1

add_rvalue_reference<T> приводит только к T&&, если T является ссылочным типом. Поэтому, когда T - void, результат равен void. Именно поэтому вы можете add_rvalue_reference<int&&> и не получить ошибку при попытке построить ссылку на ссылку. (То же самое с ссылкой lvalue.)