Do rvalue references to const have any use?
They are occasionally useful. The draft C++0x itself uses them in a few places, for example:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The above two overloads ensure that the other ref(T&)
and cref(const T&)
functions do not bind to rvalues (which would otherwise be possible).
Update
I've just checked the official standard N3290, which unfortunately isn't publicly available, and it has in 20.8 Function objects [function.objects]/p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Then I checked the most recent post-C++11 draft, which is publicly available, N3485, and in 20.8 Function objects [function.objects]/p2 it still says:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The semantics of getting a const rvalue reference (and not for =delete
) is for saying:
- we do not support the operation for lvalues!
- even though, we still copy, because we can't move the passed resource, or because there is no actual meaning for "moving" it.
The following use case could have been IMHO a good use case for rvalue reference to const, though the language decided not to take this approach (see original SO post).
The case: smart pointers constructor from raw pointer
It would usually be advisable to use make_unique
and make_shared
, but both unique_ptr
and shared_ptr
can be constructed from a raw pointer. Both constructors get the pointer by value and copy it. Both allow (i.e. in the sense of: do not prevent) a continuance usage of the original pointer passed to them in the constructor.
The following code compiles and results with double free:
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Both unique_ptr
and shared_ptr
could prevent the above if their relevant constructors would expect to get the raw pointer as a const rvalue, e.g. for unique_ptr
:
unique_ptr(T* const&& p) : ptr{p} {}
In which case the double free code above would not compile, but the following would:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Note that ptr
could still be used after it was moved, so the potential bug is not totally gone. But if user is required to call std::move
such a bug would fall into the common rule of: do not use a resource that was moved.
One can ask: OK, but why T* const&& p
?
The reason is simple, to allow creation of unique_ptr
from const pointer. Remember that const rvalue reference is more generic than just rvalue reference as it accepts both const
and non-const
. So we can allow the following:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
this wouldn't go if we would expect just rvalue reference (compilation error: cannot bind const rvalue to rvalue).
Anyhow, this is too late to propose such a thing. But this idea does present a reasonable usage of an rvalue reference to const.