reference collapsing and and tuples - c++

I'm trying to convert a parameter pack into references, because some arguments to my function can be a mix of r-/l- values.
The function in question:
//must return tuple
template <typename ...U>
std::tuple<U...> input(const char* format, U ...args) {
std::tuple<U...> t = std::tuple<U...> (args...);
//other code....
}
There is some test code that I can't touch...
This will call my function:
template <typename... U>
std::tuple<U...> test_input(const char* fmt, U &&...inp) {
input(fmt, std::forward<U>(params)...);
//other stuff...
}
And 2 test objects (also untouchable) which have deleted copy/move constructors A() and B(). as in:
A(const A &) = delete; //same for B
A &operator=(const A &) = delete; //same for B
If I call the function, as is, I'll get "deleted copy-constructor" or "deleted constructor" errors. For example:
test_input("blah blah", 1, i, "a", std::string("a"), A(123) B("string"));
The problem is that it can be any mix of r-/l-values and I don't know how to convert them to all be references
I understand that I need a reference to the arguments. I've tried using std::forward, std::forward_as_tuple, std::make_tuple, as well as changing the second parameter to input to be U & ...args and U &&...args
I also understand that I need to use reference collapsing:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
I tried to use the first and third rules to convert anything to a type of A&, but I still get errors such as: call to deleted constructor of 'B' and expects an l-value for 2nd argument
In case my question wasn't clear - How do I convert args, the second argument of input, to a tuple of references?

I think you want to do something like this:
#include <tuple>
#include <string>
//must return tuple
template <typename ...U>
std::tuple<U&&...> input(const char*, U&&...args) {
return std::tuple<U&&...>(std::forward<U>(args)...);
//other code....
}
template <typename... U>
std::tuple<U&&...> test_input(const char* fmt, U &&...inp) {
return input(fmt, std::forward<U>(inp)...);
//other stuff...
}
struct A {
A(int) { }
A(const A &) = delete; //same for B
A &operator=(const A &) = delete; //same for B
};
struct B {
B(const char *) { }
B(const B &) = delete; //same for B
B &operator=(const B &) = delete; //same for B
};
int main() {
int i = 1;
test_input("blah blah", 1, i, "a", std::string("a"), A(123), B("string"));
}
[live demo]

Related

Member Function Pointer Template Substitution Fails

I have the following code:
class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
template<class T, typename ... ARGS>
std::function<void()> * invoke(T *t, void(T::* fn)(ARGS...), ARGS... args) {
//Create a new std::function on the heap to be executed later
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}
int main(void) {
A myA;
B myB(5, 6, 7);
std::function<void()> *fn = invoke(&myA, &A::foo, myB);
}
The purpose of which is to be able to create a generic std::function from a member function pointer (and object pointer) on the heap for later execution.
My problem is, the compiler doesn't seem to be able to figure out how to expand the invoke template correctly and I get the following error:
template argument deduction/substitution failed:
inconsistent parameter pack deduction with 'const B&' and 'B'
I would like the semantics for my invoke() function to stay the same (i.e. object *, member function *, arguments...).
Is there a way to maintain these semantics while still allowing the compiler to figure out the correct template deduction?
Thanks!
EDIT
I can get it to work if I do the following:
template<class T, typename F, typename ... ARGS>
std::function<void()> * invoke(T *t, F const &fn, ARGS... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}
However, this does not fully meet my needs because two copies are made of the argument instead of one. I would like only a single copy to occur when I create the new std::function allocated on the heap. The arguments should still be passed in by reference to math the signature of the member function.
Your invoke function is already known as std::bind and if you want to ensure that parameters to foo are not copied, then combine bind with cref:
#include <functional>
struct B {
int a() const { return 1; }
};
struct A {
void foo(const B& b) {
int a = b.a();
}
};
int main(void) {
A myA;
B myB;
auto* fn = new std::function<void()>(std::bind(&A::foo, &myA, std::cref(myB)));
}
First of all, you should really be using std::forward when dealing with parameter packs. Right now you are taking all parameters as values.
Secondly, compilation fails because type deduction conflicts between void(T::* fn)(ARGS...) and ARGS... args. The compiler will get confused about whether to take types from args or from your function. For example, A::foo takes a const B& but you are giving it a value type B as well, which leads to a conflict. So you actually need two separate parameter packs to avoid this.
#include <functional>
#include <utility>
// you didn't provide a definition of B, so this is what I had to come up with
struct B {
int x;
int y;
int z;
int a() const {
return x;
}
};
class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
// you don't actually need to create a function on the heap
template<class T, typename ... FARGS, typename ...ARGS>
std::function<void()> invoke(T *t, void(T::* fn)(FARGS...), ARGS &&... args) {
return [&, t]() { (t->*fn)( std::forward<ARGS>(args)... ); };
}
int main(void) {
A myA;
B myB{5, 6, 7};
std::function<void()> fn = invoke(&myA, &A::foo, myB);
}
Alternatively, you could also use std::bind which does exactly what you are trying to accomplish:
std::function<void()> fn = std::bind(&A::foo, &myA, myB);
Arguments of invoke and arguments of your member function are not the same. One gets a const reference, the other does not. This needs to be reflected in the types.
template<class T,
typename ... ARGS,
typename ... ARGS2> // <---- !!
std::function<void()> * invoke(T *t,
void(T::* fn)(ARGS2...) // <---- !!
ARGS&&... args) { // you do want perfect forwarding
I figured out a solution that works for me based upon everyone's answers and comments.
In my application, I need to ensure that one, and only one, copy of the arguments to the function being invoked is made because that function will be executed within a different thread at a different time and the original data might not be available (e.g. a temporary variable on the stack). I had modified my code, as suggested, to use perfect forwarding within my template functions. This helped cut down on unnecessary copies significantly, however, I was still getting an extra copy within the lambda in my invoke function. It turns out, I had written the move constructor of my data type incorrectly so, when the lambda created a temporary copy of the data, it had to be copied twice.
Here are snippets of my working code (with my example datatype B):
class B {
public:
B() : _a(1), _b(2), _c(3) {
}
B(int a, int b, int c) : _a(a), _b(b), _c(c) {
}
//Copy Constructor
B(const B &v) : _a(v._a), _b(v._b), _c(v._c) {
copyCount++;
}
//Move Constructor
B(const B&& rhs): _a(rhs._a), _b(rhs._b), _c(rhs._c)
{
}
B& operator=(const B &v) {
this->_a = v._a;
this->_b = v._b;
this->_c = v._c;
copyCount++;
return *this;
}
~B() {
}
inline int a() const { return _a; }
static int copyCount;
private:
int _a;
int _b;
int _c;
};
template<class T, typename ... ARGSF, typename ... ARGS>
inline static void invoke(T *t, void(T::*fn)(ARGSF...), ARGS&&... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
//Queue in the parent thread
if (!t->_parentThread->queueInvokable(f)) delete f;
}
template<class T>
inline ConnectionHandle connect(T* t, void(T::* fn)(ARGS...)) {
const auto lambda = [=](ARGS&&... args) { T::invoke(t, fn, args...); };
return connect(lambda);
}
I have also shown here another function, connect, which saves a lambda to be called later that in turns calls the invoke function.
With all this, only a single copy of the datatype is made as long as
a) The argument to the member function (fn) is of a reference type
b) The datatype has a working move constructor

Automatic implicit generation of ctors utilising move semantics

Let we have a class with n fields. Each field can be moved. So do we have to explicitly define 2^n constructors?
The example with n=2:
struct A{
std::vector<B> a;
std::shared_ptr<B> b;
A(std::vector<B> &a, std::shared_ptr<B> &b):a(a),b(b){};
A(std::vector<B> &a, std::shared_ptr<B> &&b):a(a),b(std::move(b)){};
A(std::vector<B> &&a, std::shared_ptr<B> &b):a(std::move(a)),b(b){};
A(std::vector<B> &&a, std::shared_ptr<B> &&b):a(std::move(a)),b(std::move(b)){};
};
....
A aaa({{},{}}, shared_ptr<B>(new B()));
std::shared_ptr<B> b = ....;
A bbb({{},{}}, b);
You can work around it by having a perfect-forwarding template constructor:
struct A {
std::vector<B> a;
std::shared_ptr<B> b;
template <typename AA, typename BB> A(AA&& a, BB&& b) :
a(std::forward<AA>(a)), b(std::forward<BB>(b)) { }
};
If you need stricter parameter types requirement, you can also add enable_if or static_assert.
Here's a little explanation how perfect forwarding works:
void func1(int&&) { }
template <typename A> void func2(A&& t) {
func3(t);
func4(std::forward<A>(t);
}
template <typename B> void func3(B&&) { }
template <typename C> void func4(C&&) { }
int foo;
const int bar;
func1(foo); // ERROR
func1(bar); // ERROR
func1(std::move(foo)); // OK
func2(foo); // OK, A = int&, B = int&, C = int&
func2(bar); // OK, A = const int&, B = const int&, C = const int&
func2(std::move(foo)); // OK, A = int&&, B = int&, C = int&& <- note how && collapses to & without std::forward
You're defining the special member functions incorrectly, copy/move constructor is not defined in terms of class members, it defined in terms of the class.
instead of
class_name(const member_1&, ..., const member_n&)
class_name(const member_1&&, ..., const member_n&&)
you have to define your copy/move constructor as:
class_name(const class_name&) // copy-constructor
class_name(class_name&&) // move constructor
and inside it, use copy/move semantic. see c++draft class.copy

Generic conversion operator templates and move semantics: any universal solution?

This is a follow-up of Explicit ref-qualified conversion operator templates in action. I have experimented with many different options and I am giving some results here in an attempt to see if there is any solution eventually.
Say a class (e.g. any) needs to provide conversion to any possible type in a convenient, safe (no surprises) way that preserves move semantics. I can think of four different ways.
struct A
{
// explicit conversion operators (nice, safe?)
template<typename T> explicit operator T&& () &&;
template<typename T> explicit operator T& () &;
template<typename T> explicit operator const T& () const&;
// explicit member function (ugly, safe)
template<typename T> T&& cast() &&;
template<typename T> T& cast() &;
template<typename T> const T& cast() const&;
};
// explicit non-member function (ugly, safe)
template<typename T> T&& cast(A&&);
template<typename T> T& cast(A&);
template<typename T> const T& cast(const A&);
struct B
{
// implicit conversion operators (nice, dangerous)
template<typename T> operator T&& () &&;
template<typename T> operator T& () &;
template<typename T> operator const T& () const&;
};
The most problematic cases are to initialize an object or an rvalue reference to an object, given a temporary or an rvalue reference. Function calls work in all cases (I think) but I find them too verbose:
A a;
B b;
struct C {};
C member_move = std::move(a).cast<C>(); // U1. (ugly) OK
C member_temp = A{}.cast<C>(); // (same)
C non_member_move(cast<C>(std::move(a))); // U2. (ugly) OK
C non_member_temp(cast<C>(A{})); // (same)
So, I next experiment with conversion operators:
C direct_move_expl(std::move(a)); // 1. call to constructor of C ambiguous
C direct_temp_expl(A{}); // (same)
C direct_move_impl(std::move(b)); // 2. call to constructor of C ambiguous
C direct_temp_impl(B{}); // (same)
C copy_move_expl = std::move(a); // 3. no viable conversion from A to C
C copy_temp_expl = A{}; // (same)
C copy_move_impl = std::move(b); // 4. OK
C copy_temp_impl = B{}; // (same)
It appears that the const& overload is callable on an rvalue, which gives ambiguities, leaving copy-initialization with an implicit conversion as the only option.
However, consider the following less trivial class:
template<typename T>
struct flexi
{
static constexpr bool all() { return true; }
template<typename A, typename... B>
static constexpr bool all(A a, B... b) { return a && all(b...); }
template<typename... A>
using convert_only = typename std::enable_if<
all(std::is_convertible<A, T>{}...),
int>::type;
template<typename... A>
using explicit_only = typename std::enable_if<
!all(std::is_convertible<A, T>{}...) &&
all(std::is_constructible<T, A>{}...),
int>::type;
template<typename... A, convert_only<A...> = 0>
flexi(A&&...);
template<typename... A, explicit_only<A...> = 0>
explicit flexi(A&&...);
};
using D = flexi<int>;
which provides generic implicit or explicit constructors depending on whether the input arguments can be implicitly or explicitly converted to a certain type. Such logic is not that exotic, e.g. some implementation of std::tuple can be like that. Now, initializing a D gives
D direct_move_expl_flexi(std::move(a)); // F1. call to constructor of D ambiguous
D direct_temp_expl_flexi(A{}); // (same)
D direct_move_impl_flexi(std::move(b)); // F2. OK
D direct_temp_impl_flexi(B{}); // (same)
D copy_move_expl_flexi = std::move(a); // F3. no viable conversion from A to D
D copy_temp_expl_flexi = A{}; // (same)
D copy_move_impl_flexi = std::move(b); // F4. conversion from B to D ambiguous
D copy_temp_impl_flexi = B{}; // (same)
For different reasons, the only available option direct-initialization with an implicit conversion. However, this is exactly where implicit conversion is dangerous. b might actually contain a D, which may be a kind of container, yet the working combination is invoking D's constructor as an exact match, where b behaves like a fake element of the container, causing a runtime error or disaster.
Finally, let's try to initialize an rvalue reference:
D&& ref_direct_move_expl_flexi(std::move(a)); // R1. OK
D&& ref_direct_temp_expl_flexi(A{}); // (same)
D&& ref_direct_move_impl_flexi(std::move(b)); // R2. initialization of D&& from B ambiguous
D&& ref_direct_temp_impl_flexi(B{}); // (same)
D&& ref_copy_move_expl_flexi(std::move(a)); // R3. OK
D&& ref_copy_temp_expl_flexi(A{}); // (same)
D&& ref_copy_move_impl_flexi = std::move(b); // R4. initialization of D&& from B ambiguous
D&& ref_copy_temp_impl_flexi = B{}; // (same)
It appears that every use case has its own requirements and there is no combination that might work in all cases.
What's worse, all above results are with clang 3.3; other compilers and versions give slightly different results, again with no universal solution. For instance: live example.
So: is there any chance something might work as desired or should I give up conversion operators and stick with explicit function calls?
The C++ standard unfortunately does not have any special rule to resolve this particular ambiguity. The problem come from the fact that you are trying to overload on 2 different things: the type that the compiler is trying to convert to; and the kind of reference from which you are trying to convert from.
By introducing proxy classes, you can split the resolution in 2 steps. Step 1: decide if it's an r-value reference, an l-value reference, or a const l-value reference. Step 2: convert to any type, keeping the decision made in step 1 about the kind of reference. That way, you can use your solution with a cast() function but save you from having to specify the type:
struct A
{
class A_r_ref
{
A* a_;
public:
A_r_ref(A* a) : a_(a) {}
template <typename T> operator T&&() const&&;
};
struct A_ref
{
A* a_;
public:
A_ref(A* a) : a_(a) {}
template <typename T> operator T&() const&&;
};
struct A_const_ref
{
const A* a_;
public:
A_const_ref(const A* a) : a_(a) {}
template <typename T> operator const T&() const&&;
};
A_r_ref cast() && { return A_r_ref(this); }
A_ref cast() & { return A_ref(this); }
A_const_ref cast() const& { return A_const_ref(this); }
};

Why isn't const T && (rvalue reference) an error [duplicate]

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;
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.
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.
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.
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).
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?
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;
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.

Do rvalue references to const have any use?

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;
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.
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.
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.
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).
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?
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;
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.