Do Rvalue References to Const Have Any Use

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;

Why is const rvalue reference implicitly converted into const reference?

Some simpler code with the same reference binding:

int main()
{
const foo f;
const foo& g = std::move(f);
const foo&& h = std::move(f);
}

The expression std::move(f) is best described as an xvalue of type const foo.

So we have a reference being initialized by an xvalue of type const foo.

Both of g and h are legal and the reference binds directly. const foo&& can only bind to an rvalue, but const foo& can bind to both lvalues and rvalues. (An xvalue is an rvalue).

There is no implicit conversion as suggested in the title, the reference binds directly to the object designated by the initializer.


In the posted code there is overload resolution between two constructors with the same binding as g and h in my example. Overload resolution selects parameter type const foo&& as a better match than const foo& to an argument of type const foo and category "xvalue", since there is a ranking rule that rvalue arguments prefer rvalue references if all else is equal.

But if you remove either of those two constructors then the other one will be selected.

Can rvalue references be passed on as const references?

Yes, it is allowed.

Note, that value category of the expression a is lvalue, even though a's declared type is rvalue reference.

Furthermore, if you create an rvalue from a with std::move, your code is still well-formed, as an rvalue can be bound to a const lvalue reference:

init(std::move(a)); // std::move(a) is an rvalue (to be precise, xvalue), but still OK

C++11 rvalue reference vs const reference

std::move doesn't actually move anything out of it's own. It's just a fancy name for a cast to a T&&. Calling test like this test(std::move(x)); only shows that a T&& is implicitly convertible to a const T&. The compiler sees that test only accepts const T& so it converts the T&& returned from std::move to a const T&, that's all there is to it.

C++0x const RValue reference as function parameter

Lvalues strongly prefer binding to lvalue references, and similarly rvalue references strongly prefer binding to rvalue references. Modifiable expressions weakly prefer binding to a non-const reference.

So when your compiler is doing overload resolution, it checks if there's an overload that takes an rvalue reference, because that's preferred strongly. In this case since the experssion is a modifiable rvalue, so the rvalue reference overload wins.

There is actually use for const rvalue references, they can be used to make sure something does not bind to an rvalue. Remember that an rvalue binds to a const lvalue reference, hence if you did:

template <typename T> void foo(const T& bar) { /* ... */ }

And called the function with:

foo(createVector());

It'd work fine. However sometimes it is desired to ensure that you can only pass lvalues to a function (this is the case for std::ref for one). You can achieve this by adding an overload:

template <typename T> void foo(const T&&) = delete;

Remember, rvalues strongly prefer binding to rvalue references, and modifiable expressions prefer weakly binding to non-const references. Since we have a const rvalue-reference, it basically means that every single rvalue will bind to this, hence if you try to pass a rvalue to foo(), your compiler will give an error. This is the only way to achieve such functionality, and thus is sometimes useful.

Why is it allowed to pass R-Values by const reference but not by normal reference?

For your final question:

how can a const reference keep pointing to an R-Value (anonymous variable)

Here is the answer. The C++ language says that a local const reference prolongs the lifetime of temporary values until the end of the containing scope, but saving you the cost of a copy-construction (i.e. if you were to use an local variable instead).

Why adding `const` makes the universal reference as rvalue

The official name is not universal reference, but forwarding reference. The Standard states that only rvalue references to cv-unqualified template parameters fall in this category:

14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]

3 If P is a cv-qualified type, the top level cv-qualifiers of P’s type
are ignored for type deduction. If P is a reference type, the type
referred to by P is used for type deduction. A forwarding reference is
an rvalue reference to a cv-unqualified template parameter.
If P is a
forwarding reference and the argument is an lvalue, the type “lvalue
reference to A” is used in place of A for type deduction. [ Example:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

— end example ]

Allowing const T&& to behave as forwarding references, would make it impossible to overload a template function who takes only an rvalue reference as parameter.

Update: as @HowardHinnant mentions in the comments, const T&& does have its uses (see also this Q&A).

Does const rvalue references allow extra optimisations for compiler?

tl'dr: It doesn't enable any optimization as it doesn't guarantee in any way the object is not modified. It only adds confusion. Don't use it!


First we need to clarify what "changed by someone else" means.

  • by another thread. In this case your question is not aplicabile. You need to protect your data with a mutex or other mechanism. Otherwise the compiler can assume that no other thread modifies the data.

  • same thread, in a code not called (directly or indirectly) by the function. Not possible.

  • same thread, in a code called (directly or indirectly) by the function.

We will obviously deal with the last one:


Lets' take a simple code and look at the assembly (-O3)

auto foo(int& a)
{
a = 24;

return a;
}
foo(int&):                               # @foo(int&)
mov dword ptr [rdi], 24
mov eax, 24
ret

As you can see mov eax, 24. The returned value is set to 24. This means that the compiler can assume that no other code can modify the object referenced by a (even when a is a non-const reference).

let's add a function call code before the return:

auto bar() -> void;

auto foo(int& a)
{
a = 24;
bar();
return a;
}
foo(int&):                               # @foo(int&)
push rbx
mov rbx, rdi
mov dword ptr [rbx], 24
call bar()
mov eax, dword ptr [rbx]
pop rbx
ret

The compiler doesn't have access to the body of bar and so it has to take into account that bar could modify the object referenced by a.

Now adding const& into the equation as per your question doesn't change the equation. The object could be modified only by code called within the current function.

Having const&& doesn't change this in any way. The object referenced by a can still be modified.

As I understand rvalue reference means that given object is temporary,
so nobody else have access to its data

Not true. An rvalue reference can be bound to prvalues (temporaries) or xvalues. Your own example shows this:

operator const T && () { return std::move(val); } 

here you bound to val which is not a temporary (if the enclosing object is not).

Jonathan Wakely nailed it in a commentary:

Your example proves that const T&& doesn't have to be bound to a
temporary, and there could be several rvalue references bound to the
same thing:

Immutable<int> i{};
const int&& r1 = i;
const int&& r2 = i;

So This is no different to the const& case

And here is my take on it:

int g = 24;

auto bar() -> void { g = 11; };

auto foo(const int&& a)
{
bar();
return a;
}

auto test()
{
return foo(std::move(g));
}
test():                               # @test()
mov dword ptr [rip + g], 11
mov eax, 11
ret

The above code is valid1) and it shows that the object referenced by the const int&& parameter a is modified during the call of foo.

1) although I am not 100% sure, I am fairly certain



Related Topics



Leave a reply



Submit