How is its lifetime of a return value extended to the scope of the calling function when it is bound to a const reference in the calling function?
Asked Answered
O

4

9

"If you return a value (not a reference) from the function, then bind it to a const reference in the calling function, its lifetime would be extended to the scope of the calling function."

So: CASE A

const BoundingBox Player::GetBoundingBox(void)
{
    return BoundingBox( &GetBoundingSphere() );
}

Returns a value of type const BoundingBox from function GetBoundingBox()

variant I: (Bind it to a const reference)

const BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();

variant II: (Bind it to a const copy)

const BoundingBox l_Bbox = l_pPlayer->GetBoundingBox();

Both work fine and I don't see the l_Bbox object going out of scope. (Though, I understand in variant one, the copy constructor is not called and thus is slightly better than variant II).

Also, for comparison, I made the following changes.

CASE B

BoundingBox Player::GetBoundingBox(void)
{
    return BoundingBox( &GetBoundingSphere() );
}

with Variants: I

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();

and II:

BoundingBox l_Bbox = l_pPlayer->GetBoundingBox();

The object l_Bbox still does not go out scope. How does "bind it to a const reference in the calling function, its lifetime would be extended to the scope of the calling function", really extend the lifetime of the object to the scope of the calling function ?

Am I missing something trivial here?

Oesophagus answered 10/4, 2010 at 21:58 Comment(10)
The fact that it seems to work on your specific compiler (which is, btw ... ?) does not mean that it is going to work on any other compiler, neither that it is well defined and legal.Carsoncarstensz
@Peter: I'm using MSVC 2008. What escapes me is why / how can const have any effect on extending the lifetime. In my opinion, it prevents anyone from making making changes to the object (const correctness etc) and that is it..Oesophagus
Your Variant I looks a bit funny - does your compiler really accept that without complaint? It looks like you're initializing a reference to non-const with an rvalue, which shouldn't be allowed.Vert
@Mike: Are you talking about Case A.1 or Case B.1 Variant ?Oesophagus
Sorry, Case B.1, where you have BoundingBox& l=...Vert
@Mike Dinsdale: this non-standard behavior is something that MSVC seems to permit. This page on MSDN indicates this was fixed in MSVC 2005, but I can still bind temporaries to non-const refs in VC 2008: msdn.microsoft.com/en-us/library/cfbk5ddc(VS.80).aspxMayflower
@brainydexter: as far as your first comment goes -it's not the 'const` that's extending the lifetime, its the reference. Temporaries are not supposed to be able to be bound to non-const references (since temporaries are rvalues), but apparently MSVC is a little too forgiving in that regard.Mayflower
@Michael: So, when we do const BoundingBox& const_bBox_ref = l_Player->GetBoundingBox(); => we end up storing the temporary as the lvalue ?Oesophagus
@brainydexter: I'm not exactly sure what you mean by "we end up storing the temporary as the lvalue". When you bind a temporary to a reference, the compiler must arrange for the temporary to live somewhere until the reference goes out of scope.Mayflower
@Michael: Thanks! I think I understand it now.. After all this, I want to know what is the recommended way to return object from a function. I posted a question here: #2616607Oesophagus
M
10

Normally a temporary object (such as one returned by a function call) has a lifetime that extends to the end of the "enclosing expression". However, a temporary bound to a reference generally has it's lifetime 'promoted' to the lifetime of the reference (which may or may not be the lifetime of the calling function), but there are a couple exceptions. This is covered by the standard in 12.2/5 "Temporary objects":

The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.

See the following for more information:

An example that might help visualize what's going on:

#include <iostream>
#include <string>

class foo {
public:
    foo( std::string const& n) : name(n) { 
        std::cout << "foo ctor - " << name + " created\n"; 
    };
    foo( foo const& other) : name( other.name + " copy") { 
        std::cout << "foo copy ctor - " << name + " created\n";
    };

    ~foo() { 
        std::cout << name + " destroyed\n"; 
    };

    std::string getname() const { return name; };
    foo getcopy() const { return foo( *this); };

private:
    std::string name;
};

std::ostream& operator<<( std::ostream& strm, foo const& f) {
    strm << f.getname();
    return strm;
}


int main()
{
    foo x( "x");

    std::cout << x.getcopy() << std::endl;

    std::cout << "note that the temp has already been destroyed\n\n\n";

    foo const& ref( x.getcopy());

    std::cout << ref << std::endl;

    std::cout << "the temp won't be deleted until after this...\n\n";
    std::cout << "note that the temp has *not* been destroyed yet...\n\n";
}

Which displays:

foo ctor - x created
foo copy ctor - x copy created
x copy
x copy destroyed
note that the temp has already been destroyed


foo copy ctor - x copy created
x copy
the temp won't be deleted until after this...

note that the temp has *not* been destroyed yet...

x copy destroyed
x destroyed
Mayflower answered 10/4, 2010 at 22:14 Comment(0)
E
5

Firstly, the lifetime of temporary object gets extended to the lifetime of const reference that's bound to it, not "to the scope of the calling function" (although maybe that what you meant by that strange wording "the scope of the calling function"). This is what your CASE A illustrates, where you attach a const reference to a temporary. The temporary continues to live as long as the reference lives. When the reference ends its lifetime, the temporary object gets destroyed as well.

Secondly, your CASE B is simply ill-formed, non-compilable. Namely, the

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

is illegal. It is illegal in C++ to attach a non-const reference to a temporary. If your compiler allows it, it must be a quirk/extension of your compiler, which has little to do with C++ language.

Embryonic answered 10/4, 2010 at 22:22 Comment(0)
R
2

The point is that when returning by value, the value is copied into the variable you are assigning the result of the function. (just like you said - the copy constructor is called). No lifetime extension, you just create a brand-new object.

When returning by reference, under the hood you just pass the pointer to the variable defined in the function. So, a new object is not created, you just have reference to it outside the function. With that case the lifetime of an function-inside variable is extended.

Ransdell answered 10/4, 2010 at 22:4 Comment(0)
A
1

Usually, if you return an object by value from a function, the said object will be destroyed when the assignment expression is finished:

myclass X = getX(); // after copy constructor, the returned value is destroyed
                    // (but you still hold a copy in X)

In the case you describe, the returned value will be destroyed later on, allowing you to use it:

const myclass& X = getX();
cout << X.a << endl; // still can access the returned value, it's not destroyed
Antler answered 10/4, 2010 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.