R-value references are a type of reference which supports references to temporary objects such as as the ones returned from functions.

The main uses of r-value references are:

  • “move” the inner part of an object from a source to a destination instance to avoid copying data from an object that will be destroyed anyway just after the assignment or construction statement is executed
  • transfer ownership (i.e. the responsibility to delete an object)

So why would anyone want to use a constant r-value reference which prevents the move action to take place?

Because if you are holding a reference to an object for future use you do not want to assign to it a reference to a temporary object which will disappear just after the assignment operation and there is one case where in order to prevent the assignment of a reference to a temporary object you need to deal with constant r-value references.

Consider the following code:

#include <iostream>

struct B {
    B(int i = 1) : i_(new int(i)) {}
    int* i_;
    ~B() {
        delete i_;
        i_ = nullptr;
        std::cout << "~B()" << std::endl;
    }
};


class C {
public:
    C(const B& b) : bref_(b) {}
    const B& b() const { return bref_; }
private:
    const B& bref_;
};

B NewB() {
    return B();
}

int main(int, char**) {
    C c(NewB());
    std::cout << *c.b().i_ << std::endl;
    return 0;
}

Compiling and running the above code results in an error:

~B()
Segmentation fault: 11

This is because the temporary reference to the B object points to an object that has been destroyed.

In order to intercept such kind of errors you can add an explicit copy constructor receiving an r-value reference which matched the temporary object returned by the NewB function and throw and exception or report an error:

class C {
public:
    C(const B& b) : bref_(b) {}
    const B& b() const { return bref_; }
    C(B&& b) : bref_(b) {
        std::cerr << "#####" << std::endl;
        throw std::runtime_error("Reference to temporary object");
    }
private:
    const B& bref_;
};
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: Referenc
e to temporary object
Abort trap: 6

It would be much better however, to report a compile-time error, which is what you achieve by explicitly deleting the r-value reference constructor:

class C {
public:
    C(const B& b) : bref_(b) {}
    const B& b() const { return bref_; }
    C(B&& b) = delete;
private:
    const B& bref_;
};

Now a proper compile time error is reported:

bash-3.2$ clang++ -std=c++11 s1.cpp
s1.cpp:29:7: error: call to deleted constructor of 'C'
    C c(NewB());
      ^ ~~~~~~
s1.cpp:19:5: note: 'C' has been explicitly marked deleted here
    C(B&& b) = delete;
    ^
1 error generated.

Great, so we fixed the problem!

…not quite; try to change the return type of the NewB function from B to const B:

const B NewB() {
    return B();
}
bash-3.2$ ./a.out
~B()
Segmentation fault: 11

What??

That’s because in this case although the returned value is a temporary object, being of const type it matched the standard const reference constructor.

Easy to fix though: just explicitly delete the C(const B&&) constructor:

class C {
public:
    C(const B& b) : bref_(b) {}
    const B& b() const { return bref_; }
    C(B&& b) = delete;
    C(const B&& b) = delete;
private:
    const B& bref_;
};

Again, error at compile time:

bash-3.2$ clang++ -std=c++11 s1.cpp
s1.cpp:30:7: error: call to deleted constructor of 'C'
    C c(NewB());
      ^ ~~~~~~
s1.cpp:20:5: note: 'C' has been explicitly marked deleted here
    C(const B&& b) = delete;
    ^
1 error generated.

Now we have covered all the cases.

A word on templates

In case you are dealing with template callable objects, i.e. instead of B& references you have T& references where T is a template parameter, you do not need to add an explicit delete for the const T&& case, this is because when a callable object returns a constant value passed to another callable object the value is already of type const U and it automatically invokes the T&& callable object where T is a constant type of type const U.

To summarize: if you pass an object of type const U to T&& you end up with const U&&, and if you pass it to const T&& you get const const U&&.

As an exercise try to have two versions of the same template function with && and const && signatures and write code that invokes the const && version.

In order to verify that removing the T&& version of callable entities is enough to avoid receiving constant temporary instances I did replace theconst T&& signatures with T&& in clang 3.3 standard library’s std::(c) ref functions and got exactly the same results whether I use T&& or const T&&.

Here is the compiler output when compiling code that tries to create a reference to a constant temporary object obtained after changing the std::cref signature to T&&:

...
../const-r-value-ref.cpp:49:16: error: call to deleted function 'cref'
auto ref = std::cref(([] {
^~~~~~~~~
/usr/bin/../lib/c++/v1/__functional_base:425:27: note: candidate function [with _Tp = const C] has been explicitly deleted
template <class _Tp> void cref(_Tp&&) = delete;
...

pay attention to with _Tp = const C and void cref(_Tp&&) = delete.

What happened is that std::cref got a constant instance and substituted the template parameter with a constant type, so no need to delete theconst && version here because the case is already covered by the && overload.

No idea why in the standard library they are deleting the const T&& overload, but there probably is a reason, I just do not see it.

Code

Feel free to play with the code available here experimenting with the various compilation options (read the comments).