I guess not, but I would like to confirm. Is there any use for const Foo&&
, where Foo
is a class type?
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;
const T&&
is used? –
Unpaid diff
to verify the first and last files are equal, and that the second is different. –
Advowson The above two overloads...
I don't quite follow, but it seems like an important point. –
Rev T
is NOT a template parameter, but an explicitly given type such as class Bar; void Foo(Bar &&)
and class Bar; void Foo(Bar const &&)
? Does those two Foo
make any difference? –
Maccarone T&&
sufficient? –
Periphrasis const T&&
prevents somebody foolishly using explicit template args of the form ref<const A&>(...)
. That's not a terribly strong argument, but the cost of const T&&
over T&&
is pretty minimal. –
Terceira A const
, such as A const test();
, then ref(test())
would bind to ref(T&)
without the protective overload because T
would deduce as A const
, and then ref(A const&)
can bind to an rvalue. Easy case to miss since returning a const
type is typically contra-indicated as it messed up move semantics. But a long time ago, before move semantics, returning const
types was actually considered good style as it prevented assignment into temporaries. –
Terceira 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.
Besides std::ref, the standard library also uses const rvalue reference in std::as_const for the same purpose.
template <class T>
void as_const(const T&&) = delete;
It is also used as return value in std::optional when getting the wrapped value:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
As well as in std::get:
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
This is presumably in order to maintain the value category as well as constness of the wrapper when accessing the wrapped value.
This makes a difference whether const rvalue ref-qualified functions can be called on the wrapped object. That said, I don't know any uses for const rvalue ref qualified functions.
They are allowed and even functions ranked based on const
, but since you can't move from const object referred by const Foo&&
, they aren't useful.
const T&, T&, const T&&, T&&
–
Integer const
means that the object itself is not changed, apart from mutable, static members (e.g. an std::map<T*, T*>
that indicates current owner for a type that's aware of its instances) might change. In fact it's useful if you're const-correct since then you might still use the old object. –
Thompkins operator = (const Foo &&x)
does not need to worry about the possibility that &x == this
. –
Paluas Rvalue references are meant to allow moving data. So in the vast majority of case its use is pointless.
The main edge case you will find it is to prevent people to call a function with an rvalue:
template<class T>
void fun(const T&& a) = delete;
The const version will cover all the edge cases, contrary to the non const version.
Here is why, consider this example:
struct My_object {
int a;
};
template<class T>
void fun(const T& param) {
std::cout << "const My_object& param == " << param.a << std::endl;
}
template<class T>
void fun( T& param) {
std::cout << "My_object& param == " << param.a << std::endl;
}
int main() {
My_object obj = {42};
fun( obj );
// output: My_object& param == 42
const My_object const_obj = {64};
fun( const_obj );
// output: const My_object& param == 64
fun( My_object{66} );
// const My_object& param == 66
return 0;
}
Now if you'd like to prevent someone using fun( My_object{66} );
since in the present case, it will be converted to const My_object&, you need to define:
template<class T>
void fun(T&& a) = delete;
And now fun( My_object{66} );
will throw an error, however, if some smarty pants programmer decides to write:
fun<const My_object&>( My_object{1024} );
// const My_object& param == 1024
This will work again and call the const lvalue overload version of that function... Fortunately we can put an end to such profanity adding const to our deleted overload:
template<class T>
void fun(const T&& a) = delete;
I can't think of a situation where this would be useful directly, but it might be used indirectly:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
The T in g is T const, so f's x is an T const&&.
It is likely this results in a comile error in f (when it tries to move or use the object), but f could take an rvalue-ref so that it cannot be called on lvalues, without modifying the rvalue (as in the too simple example above).
It's somewhat disturbing how pretty much everyone in this thread (with the exception of @FredNurk and @lorro) misunderstand how const
works, so allow me to chime in.
Const reference only forbids modifying the immediate contents of the class. Not only do we have static and mutable members which we very well can modify through a const reference; but we also can modify the contents of the class stored in a memory location referenced by a non-static, non-mutable pointer - as long as we don't modify the pointer itself.
Which is exactly the case of an extremely common Pimpl idiom. Consider:
// MyClass.h
class MyClass
{
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};
// MyClass.cpp
class MyClass::Pimpl
{
public:
int meat {42};
};
MyClass::MyClass() : impl {new Pimpl} { }
MyClass::MyClass(int g_meat) : MyClass()
{
impl->meat = g_meat;
}
MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}
MyClass::~MyClass()
{
delete impl;
}
int MyClass::GetMeat() const
{
return impl->meat;
}
// main.cpp
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
Behold - a fully functional, const-correct move constructor which accepts const rvalue references.
Perhaps it could be considered useful in this context (coliru link):
#include <iostream>
// Just a simple class
class A {
public:
explicit A(const int a) : a_(a) {}
int a() const { return a_; }
private:
int a_;
};
// Returning a const value - shouldn't really do this
const A makeA(const int a) {
return A{a};
}
// A wrapper class referencing A
class B {
public:
explicit B(const A& a) : a_(a) {}
explicit B(A&& a) = delete;
// Deleting the const&& prevents this mistake from compiling
//explicit B(const A&& a) = delete;
int a() const { return a_.a(); }
private:
const A& a_;
};
int main()
{
// This is a mistake since makeA returns a temporary that B
// attempts to reference.
auto b = B{makeA(3)};
std::cout << b.a();
}
It prevents the mistake being compiled. There are clearly a bunch of other problems with this code that compiler warnings do pick up, but perhaps the const&&
helps?
© 2022 - 2024 — McMap. All rights reserved.
const&&
are very important, although he doesn't say why: youtube.com/watch?v=JhgWFYfdIho#t=54m20s – Bowles