skypjack skypjack - 3 months ago 16
C++ Question

Is std::declval<void>() a valid expression?

As far as I know, I cannot declare an rvalue reference to

void
.

As an example, the following code is ill-formed:

void f(void &&v) { }


From [20.2.6/1] (function template declval) we have a declaration for
declval
that is:

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


Thus,
declval<void>
(let me say) would result in
void &&
, that I guessed it was ill-formed as well as in the previous example.

Anyway, the following minimal, working example compiles:

#include<utility>

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


Note that the following is true too:

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


I would have expected it to be
void&&
as previously mentioned (or better, I was expecting it fails to compile).

Actually, it happens to be an rvalue reference for any other non-reference type.

As an example:

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


Is
declval<void>
a valid expression or not? Is the code above legal?

Why does the behavior in case of
void
is different than with any other type? (For it wouldn't have worked otherwise could be an answer, if the code is legal).

If it's legal, where does the standard allow that? I've not been able to find the case.

Of course, the standard says:


The template parameter T of declval may be an incomplete type.


Anyway, here it would result in a non acceptable type (
void&&
) and it works around it discarding the rvalue reference.

Answer

add_rvalue_reference<T> only results in T&& if T is a referenceable type. So when T is void, the result is just void. This is also why you can add_rvalue_reference<int&&> and not get an error attempting to construct a reference to a reference. (Same with lvalue reference.)

Comments