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?
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
}