Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do *non*-const references prolong the lives of temporaries?

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?

like image 837
dlf Avatar asked May 15 '14 17:05

dlf


People also ask

Can a const reference be bound to a non-const object?

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 .

Can a const reference call a non-const function?

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.

Does const reference copy?

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 .

Are references const?

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.


2 Answers

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 be const), 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.

like image 143
Praetorian Avatar answered Oct 01 '22 11:10

Praetorian


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.)

like image 29
James Kanze Avatar answered Oct 01 '22 10:10

James Kanze