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?
"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?
Solution 1:
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:
- C++ constant reference lifetime (container adaptor)
- GotW #88: A Candidate For the "Most Important const"
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
Solution 2:
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.
Solution 3:
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.
Solution 4:
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