4

Below code shows lifetime of object created in function create() is extended to the life time of const ref created in main, is this correct in all cases? I mean we can extend the life time of temporary in certain cases by creating a reference to it? Or in this specific case the compiler is misbehaving?

It is compiled with MSVC2005

#include <iostream>

class testClass
{
public:
    testClass()
    {
        std::cout << "in testClass " << ((void*)this) << std::endl;
    }

    ~testClass()
    {
        std::cout << "in ~testClass " << ((void*)this) << std::endl;
    }
};


testClass create()
{
    return testClass();
}


int main()
{
    {
        testClass const& obj = create();

        std::cout << "we got a const reference to obj " << ((void*)&obj) << std::endl;
    }

    return 0;
}

Output

in testClass 0018FF13
we got a const reference to obj 0018FF13
in ~testClass 0018FF13

Of course other may get different addresses...In above case i was expecting destructor for the object created with function create(), will be called before line

std::cout << "we got a const reference to obj " << ((void*)&obj) << std::endl; 

is executed.

Saqlain
  • 17,490
  • 4
  • 27
  • 33
  • so.. what is the output? – default Apr 17 '13 at 07:52
  • "extend the lifetime of temporaries" - why? In any case, doesn't RVO elide the temporary? – Roger Rowland Apr 17 '13 at 07:52
  • Related: [Does a const reference prolong the life of a temporary?](http://stackoverflow.com/questions/2784262/does-a-const-reference-prolong-the-life-of-a-temporary) – Thomas Moulard Apr 17 '13 at 07:53
  • 1
    what output were you expecting? – default Apr 17 '13 at 07:53
  • Of course i would expect testClass destructor should be called before line "std::cout << "we got a const reference to obj " << ((void*)&obj) << std::endl;" – Saqlain Apr 17 '13 at 07:56
  • @RogerRowland: nominally there are two temporaries, and RVO eliminates one of them (the one that appears as `testClass()` in the `return` statement of `create()`, and the temporary that is the return value of `create()`. RVO eliminates one of them. If the code in `main()` were changed to `testClass obj = f()`, then both temporaries could be elided and the output would be the same as the questioner saw. So there aren't *many* uses for extending the lifetime of a temporary in this way since often an elided copy will do just as well. – Steve Jessop Apr 17 '13 at 09:29

3 Answers3

6

This is a special case: binding a const reference to a temporary object, stretches its lifetime until that const reference goes out of scope. This is only true for function local const references, e.g. the following will not work:

struct X
{
  int const& i
  X(int const& i_) : i(i_) {}
};

int f();

int main()
{
  X x(f()); 
  int u = x.i; //!
}

During construction of x, the i_ will be bound to the temporary returned by f, as will i, but although it's a const reference, that temporarie's lifetime will not be stretched to that of i, i.e. the rule does apply here.

See this GOTW article

Update: as is mentioned in the article and in the comments, the const is vital. The C++ standard allows binding of temporaries only to const lvalue references and rvalue references, so int& i = f(); is not allowed. However, MSVC has an extension that allows this, and as with other references, the lifetime of the temporary is extended until the reference goes out of scope. I would not recommend to exploit that extension, as it makes the code nonportable. In fact, I would be careful binding temporaries to references, since this feature is not well known and your colleagues might be baffled seeing it work, which means the code will lack readability.

David G
  • 94,763
  • 41
  • 167
  • 253
Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Just to confirm this applies for only const reference? – Saqlain Apr 17 '13 at 08:02
  • 1
    yes. `int & i = f();` without that const will lead to a dangling reference immediately. See the linked article! – Arne Mertz Apr 17 '13 at 08:03
  • @Arne: it's some irritating MSVC extension to allow binding an rvalue expression to a non-const reference at all: the standard doesn't actually specify that only const references extend the lifetime of temporaries, it just says "references". But as soon as you use an extension the standard is out the window, and as far as the standard is concerned the reference necessarily is const (or an rvalue reference in C++11). – Steve Jessop Apr 17 '13 at 09:23
  • @SteveJessop Thanks for the hint. I knew of the extension but didn't think it was worth mentioning (because it's not portable). I will edit my answer. – Arne Mertz Apr 17 '13 at 09:46
2

To clarify - We can show 3 scenarios for testClass create():

1

Returning a copy but catching it by const reference

testClass create()
{
    return testClass();
}

testClass const &obj = create();

It extends the life time of temporary testClass() as long as obj.

 

2

Returning a copy and catching it by assignment (RVO)

testClass create()
{
    return testClass();
}

testClass obj = create();

It extends the life time of temporary testClass() as long as obj, because RVO implicitly applies on it. It'd better to say, in fact there is no temporary object here, all things operate on obj even in create() function.

 

3

Returning a copy and catching it by assignment (without RVO)

testClass create()
{
    return testClass();
}

testClass obj = create();

The life time of temporary testClass() exceeds after returning from create(), and a new object comes to world.

masoud
  • 55,379
  • 16
  • 141
  • 208
  • RVO is not explicitly part of the C++ standard, although it states that unnesessary copies may be omitted in certain circumstances. In the sense of standard understanding of object lifetimes, `obj` is a new object regardless of wether RVO kicks in or not. But since the OP explicitly asked about references, i.e. the unusual case, that does not really matter here ;-) – Arne Mertz Apr 17 '13 at 08:16
1

This link should help you to understand how this situation is qualified.

When a temporary object is created to initialize a reference variable, the name of the temporary object has the same scope as that of the reference variable. When a temporary object is created during the evaluation of a full-expression (an expression that is not a subexpression of another expression), it is destroyed as the last step in its evaluation that lexically contains the point where it was created.

There are two exceptions in the destruction of full-expressions:

  • The expression appears as an initializer for a declaration defining an object: the temporary object is destroyed when the initialization is complete.
  • A reference is bound to a temporary object: the temporary object is destroyed at the end of the reference's lifetime.
Roman Nikitchenko
  • 12,800
  • 7
  • 74
  • 110