When is temporary lifetime extension useful in modern C++?
Asked Answered
S

1

11

In C++ you can bind return value of a function (which return value, not reference) to const reference and code will still be valid because lifetime of this temporary will be prolonged till the end of scope. For example

std::string get_string() {
    return "abc";
}


void f() {
    const std::string& str = get_string();
    std::cout << str; // valid, str is not dangling reference.
}

My question is, when is it useful, e.g. when is code like

A get_a();
const A& a = get_a();

better than the code

A get_a();
A a = get_a();

and in what way (e.g. faster, smaller binary size, etc)? What should be the implementations of A, get_a and code after calling get_a?

I've tested several cases by hand and in each case it seems to have the same number of copies and moves.

Let's restrict this question to current C++ standard, modern versions of compilers and builds with optimisation enabled (O2, O3 or equivalents for other compilers)

Stereo answered 4/6, 2018 at 4:2 Comment(0)
B
10

Deliberate lifetime extension of prvalues into named stack variables like this is not useful if you know exactly what's going on. Which means it is useful if you don't know exactly what's going on.

Say, you're in a template function. And the user is supposed to give you some object that has a get member function that returns some type that fits some expected behavior. Question: does get return a reference or a prvalue?

Answer: you don't care. It doesn't matter if it returns a prvalue or a reference; what matters is that what it returns can be manipulated in the fashion you expect. So you might expect obj.get() = 10; to work, for example.

Maybe get returns a reference to an object. Or maybe it returns a prvalue that is a proxy object that acts like a reference. In the above case, maybe it has an operator= overload, so that you can assign to it. You the user don't care.

So, what happens if you want to store what get returns for a (short) period of time? Well, you don't want to do auto x = obj.get();; if it returned an actual reference, you would get a copy of the reference, which is probably not what you wanted. So you do auto &&x = obj.get();. Lifetime extension allows this to work just as well with proxy prvalue objects as with actual references.

Briarroot answered 4/6, 2018 at 4:17 Comment(3)
Nice post. Excuse my ignorance, but why would getting a copy of a reference be a bad thing? In what way does it not behave in the same way as the original? I realise that auto && can bind to anything and so is more adaptable but that's not the same thing. But perhaps I misunderstand or perhaps I'm just picking nits. Learnt some interesting stuff about auto && here.Zadazadack
@PaulSanders: If you expect to be able to do obj.get() = 5; to assign a value to obj, will auto x = obj.get(); x = 5; do the same? If the return of get is a proxy, then yes. If the return of get is a language reference, then no. You use auto&& to indicate that you want to store exactly what get returns.Briarroot
OK, I still didn't understand this so I knocked up a demo, and given (say) int& get() { ... } ... auto x = obj.get(); you can indeed not assign to obj via x. But why not? Found my answer here: When auto is deduced, it is not deduced to the reference. It always deduces to the value. That is the key phrase here. My, you have to be so careful with this language.Zadazadack

© 2022 - 2024 — McMap. All rights reserved.