Once upon a time, I assumed that code like this would fail:
const MyClass& obj = MyClass();
obj.DoSomething();
because the MyClass object would be destroyed at the end of its full-expression, leaving obj as a dangling reference. However, I learned (here) that this isn't true; the standard actually has a special provision that allows const references to keep temporaries alive until said references are destroyed themselves. But, it was emphasized, only const references have this power. Today I ran the code below in VS2012 as an experiment.
struct Foo
{
Foo() { std::cout << "ctor" << std::endl; }
~Foo() { std::cout << "dtor" << std::endl; }
};
void f()
{
Foo& f = Foo();
std::cout << "Hello world" << std::endl;
}
The output when calling f()
was:
ctor
Hello world
dtor
So I had a look at the C++11 draft standard, and only found this (§ 12.2/4):
There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context [doesn't apply]. The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference.
The word const
is conspicuously absent from the above. So; has this behavior been changed for C++11, was I wrong about the const
thing to begin with, or does VS2012 have a bug and I just haven't found the relevant part of the standard?
No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .
Once you have a const object, it cannot be assigned to a non-const reference or use functions that are known to be capable of changing the state of the object.
Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .
Because the reference is a const reference the function body cannot directly change the value of that object. This has a similar property to passing by value where the function body also cannot change the value of the object that was passed in, in this case because the parameter is a copy.
The behavior hasn't changed, you just need to turn your warning level up to /W4
. VisualStudio implements the lifetime extension rule even for non-const
lvalue references as a compiler extension. In this context, binding an rvalue to the non-const
reference behaves the same as if you were binding it to a const
reference.
With /W4
you'd see this:
warning C4239: nonstandard extension used : 'initializing' : conversion from 'Foo' to 'Foo &'
1> A non-const reference may only be bound to an lvalue
The text disallowing binding of an rvalue to a non-const
lvalue reference can be found in §8.5.3/5
— Otherwise, the reference shall be an lvalue reference to a non-volatile
const
type (i.e., cv1 shall beconst
), or the reference shall be an rvalue reference.
[ Example:double& rd2 = 2.0; // error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const
—end example ]
The second half of the quoted statement is what allows binding of a temporary to an rvalue reference, as shown in litb's answer.
string &&s = string("hello");
This, combined with the lifetime extension rule in §12.2/5, means the lifetime of the temporary will now match the lifetime of the (rvalue) reference it is bound to.
The word const
was never present in this section. The rule
has always been (from as long as I can remember) that
a temporary used to initialize a reference has its lifetime
extended to match that of the reference, regardless of the type
of the reference.
Sometime in the late 1980's (very pre-standard), C++ introduced the rule that a temporary could not be used to initialize a non-const reference. Initializing a non-const reference with a temporary would still extend the lifetime (presumably), but since you couldn't do it... Most compilers implemented a transition period, in which such an initialization would only emit a warning (and the lifetime was extended).
For some reason, when Microsoft finally decided to implement C++ (some time in the early 1990's), they decided not to implement the new rule, and allowed initialization of a non-const reference with a temporary (without even a warning, at a time when most other vendors were gradually turning the warning into an error). And of course, the implemented the usual lifetime rule.
Finally, in C++11, new types of references were introduced, which allowed (or even required) initialization with a temporary. The rule about the lifetime of temporaries hasn't changed, though; a temporary which is used to initialize a reference (regardless of the type of reference) has its lifetime extended.
(With a few exceptions: I would not recommend using a temporary to initialize a class member reference in an initialization list.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With