6

In C++11 a const lvalue reference can be initialized with a mutable rvalue reference. The value referred to by the rvalue can then change producing a visible mutation to what the const lvalue is referring to. Here is an example:

int && rval = 3;
const int & lval = rval;

cout << "lval = " << lval << endl;
cout << "rval = " << rval << endl;

rval ++;

cout << "lval = " << lval << endl;

Output (from clang 3.2 and gcc 4.8.2 both with -std=c++11):

lval = 3
rval = 3
lval = 4

I would guess the reason for this is that the referent cannot be modified through the lvalue reference but it can be modified through the rvalue reference. But, I don't understand why a const lvalue is allowed to refer to a mutable object.

Can someone explain the rationale for this and give best practices for when dealing with such a situation. Also, are there other similar examples where constness can be subverted?

user2141130
  • 954
  • 7
  • 22

1 Answers1

16

But, I don't understand why a const lvalue is allowed to refer to a mutable object

Having a const reference to something only means that you can't modify the object through that reference. It doesn't mean that nobody is allowed to change the object ever.

Let's say you have a function:

void f(Foo const& bar);

The function is declaring to the caller that it will not modify bar. That's it. Nothing more than that, nothing less than that. It says nothing about what happens to bar while f is executing (e.g. in another thread); the language doesn't have a way to express constraints like that.

One last bit here:

int && rval = 3;

rval is an lvalue. It has a name and can be on the left side of an assignment, as your code clearly demonstrates. The difference between an rvalue reference and an lvalue reference is that rvalue references can bind to rvalues -- not that they themselves are rvalues.

This is why given something like

void foo(unique_ptr<X>&& x)
{
    aVector.emplace_back(x);
}

does not compile. x is declared as an rvalue reference, but inside foo it has a name and can be on the left side of an assignment, and is an lvalue. Moving it into something else requires using move or similar:

void foo(unique_ptr<X>&& x)
{
    aVector.emplace_back(move(x)); // Ok
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • And which one of the 8 types, at §3.10/10 is it, in this case? – Shoe Jan 07 '14 at 21:39
  • Ok, I now understand that rvalue references are lvalues that can bind to rvalues -- thanks. I am still a bit unclear on constness. I think my confusion stems from the disconnect between the mutability in this case and the illegality of this: `const int x=3; int & lval = x;` My guess: if an object is originally defined as a const, then only const names may refer to it (and that object would not be mutable under any circumstances). Is that close? – user2141130 Jan 07 '14 at 21:49
  • @Jefffrey: I'm not an expert on the value categories -- I'm not that big a standards-lawyer. :) – Billy ONeal Jan 07 '14 at 21:51
  • @user: In that case you are *removing* `const` which isn't legal. In this case you are adding `const`, which is fine. This is no different than `int x = 3; int const& lval = x;`. – Billy ONeal Jan 07 '14 at 21:52
  • @Billy I see. Then `x++;` is legal and `lval++;` is not. And just so I can be sure, are there any exceptions where *removing* const is legal? – user2141130 Jan 07 '14 at 22:01
  • @user: `const_cast`. :) – Billy ONeal Jan 07 '14 at 22:44