Question

I'm not arguing against the result of the code below, for I think it's correct to assume that a const lvalue reference and an rvalue reference, both extend the lifetime of the temporary returned from the function. What surprises me is this paragraph in the Standard, which appears to say the contrary:

12.2p5 (emphasis mine):

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 except:

  • ...
  • ...
  • The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

Example code:

#include <iostream>

struct A{ A() : i(2) {} int i;};

A f() { A a; return a; }

int main()
{
    A&& a1 = f();
    std::cout << a1.i << '\n';
    const A& a2 = f();
    std::cout << a2.i << '\n';
}
Was it helpful?

Solution 2

The second context is when a reference is bound to a temporary - except - The lifetime of a temporary bound to the returned value in a function return statement is not extended

A f() { A a; return a; }

First of all, a isn't a temporary. This may be what you were thinking of:

A f() { return A(); }

Secondly, the return type of the function is not a reference type. Here is when the rule would apply:

const A& f() { return A(); }

The temporary from A() is being bound to the return type of const A&. As per the rule, the lifetime of the temporary is not extended.

OTHER TIPS

The quote you mention is specifically for returning a reference from a function and binding that reference to a temporary:

const T& f() { return T(); };

That is not the case in your code because you are binding a temporary to a reference at the call side, not in the return statement. In your comments you mention that when you modify the code to:

T f() { return T(); }
T&& r = f();

the lifetime is still extended, but that is wrong. The temporary lives for the duration of the return statement during which it gets copied to the returned value. After the copy completes the lifetime of the temporary ends. On the calling side you have a different temporary (the result of f()) whose lifetime gets extended.

But there's no doubt that the lifetime of temporaries are extended. If you define a destructor for class A with any message, it will be printed at the end of main(), or is there?

That statement is also incorrect. You are seeing the effects of the return value optimization (RVO). Instead of creating a temporary for the T() inside the function and another for the returned value, the compiler is creating the two objects in the same location. You are probably seeing a single object in the output of the program, but in theory there are two.

You can try using g++ with -fno-elide-constructors and you should be able to see both temporaries, one of which is extended the other will not be.

Alternatively, you can return a reference:

const A& f() { return A(); }
const A& r = f();

Which should show how the temporary dies before r goes out of scope.


This is basically the same test inhibiting the

$ g++ --version | head -1

g++ (GCC) 4.3.2

$ cat x.cpp

#include <iostream>

struct X {
    X() { std::cout << "X\n"; }
    ~X() { std::cout << "~X\n"; }
};

X f() { return X(); }

int main() {
    const X& x = f();
    std::cout << "still in main()\n";
}

$ g++ -o t1 x.cpp && ./t1

X
still in main()
~X

$ g++ -fno-elide-constructors -o t2 x.cpp && ./t2

X
~X
still in main()
~X

$ clang++ -version | head -1

$ clang version 3.2 (tags/RELEASE_32/final)

$ clang++ -fno-elide-constructors -o t3 x.cpp && ./t3

X
~X
still in main()
~X

$ cat y.cpp

#include <iostream>

struct X {
    X() { std::cout << "X\n"; }
    ~X() { std::cout << "~X\n"; }
};

const X& f() { return X(); }

int main() {
    const X& x = f();
    std::cout << "still in main()\n";
}

$ g++ -fno-elide-constructors -o t4 y.cpp && ./t4

X
~X
still in main()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top