passing value to reference works one way [duplicate] - c++

struct A {
A(int) : i(new int(783)) {
std::cout << "a ctor" << std::endl;
}
A(const A& other) : i(new int(*(other.i))) {
std::cout << "a copy ctor" << std::endl;
}
~A() {
std::cout << "a dtor" << std::endl;
delete i;
}
void get() {
std::cout << *i << std::endl;
}
private:
int* i;
};
const A& foo() {
return A(32);
}
const A& foo_2() {
return 6;
}
int main()
{
A a = foo();
a.get();
}
I know, returning references to local values is bad. But, on the other hand, const reference should extend a temporary object lifetime.
This code produce an UB output. So no life extention.
Why? I mean can someone explain whats happening step by step?
Where is fault in my reasoning chain?
foo():
A(32) - ctor
return A(32) - a const reference to local object is created and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)
foo_2():
return 6 - temp object of type A is created implicitly,a const reference to this object is created(extending its life) and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)

Rules of temporary lifetime extension for each specific context are explicitly spelled out in the language specification. And it says that
12.2 Temporary objects
5 The second context is when a reference is bound to a temporary. [...] A temporary bound to the returned value in a function return statement
(6.6.3) persists until the function exits. [...]
Your temporary object is destroyed at the moment of function exit. That happens before the initialization of the recipient object begins.
You seem to assume that your temporary should somehow live longer than that. Apparently you are trying to apply the rule that says that the temporary should survive until the end of the full expression. But that rule does not apply to temporaries created inside functions. Such temporaries' lifetimes are governed by their own, dedicated rules.
Both your foo and your foo_2 produce undefined behavior, if someone attempts to use the returned reference.

You are misinterpeting "until function exit". If you really want to use a const reference to extend the life of an object beyond foo, use
A foo() {
return A(32);
}
int main() {
const A& a = foo();
}
You must return from foo by value, and then use a const reference to reference the return value, if you wish to extend things in the way you expect.
As #AndreyT has said, the object is destroyed in the function that has the const &. You want your object to survive beyond foo, and hence you should not have const &
(or &) anywhere in foo or in the return type of foo. The first mention of const & should be in main, as that is the function that should keep the object alive.
You might think this return-by-value code is slow as there appear to be copies of A made in the return, but this is incorrect. In most cases, the compiler can construct A only once, in its final location (i.e. on the stack of the calling function), and then set up the relevant reference.

Related

Create a non-temporary object in a constructor in c++ [duplicate]

I know that a temporary cannot be bound to a non-const reference, but it can be bound to const reference. That is,
A & x = A(); //error
const A & y = A(); //ok
I also know that in the second case (above), the lifetime of the temporary created out of A() extends till the lifetime of const reference (i.e y).
But my question is:
Can the const reference which is bound to a temporary, be further bound to yet another const reference, extending the lifetime of the temporary till the lifetime of second object?
I tried this and it didn't work. I don't exactly understand this. I wrote this code:
struct A
{
A() { std::cout << " A()" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
};
struct B
{
const A & a;
B(const A & a) : a(a) { std::cout << " B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
};
int main()
{
{
A a;
B b(a);
}
std::cout << "-----" << std::endl;
{
B b((A())); //extra braces are needed!
}
}
Output (ideone):
A()
B()
~B()
~A()
-----
A()
B()
~A()
~B()
Difference in output? Why the temporary object A() is destructed before the object b in the second case? Does the Standard (C++03) talks about this behavior?
The standard considers two circumstances under which the lifetime of a temporary is extended:
§12.2/4 There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when an expression appears as an initializer for a declarator defining an object. In that context, the temporary that holds the result of the expression shall persist until the object’s initialization is complete. [...]
§12.2/5 The second context is when a reference is bound to a temporary. [...]
None of those two allow you to extend the lifetime of the temporary by a later binding of the reference to another const reference. But ignore the standarese and think of what is going on:
Temporaries are created in the stack. Well, technically, the calling convention might mean that a returned value (temporary) that fits in the registers might not even be created in the stack, but bear with me. When you bind a constant reference to a temporary the compiler semantically creates a hidden named variable (that is why the copy constructor needs to be accessible, even if it is not called) and binds the reference to that variable. Whether the copy is actually made or elided is a detail: what we have is an unnamed local variable and a reference to it.
If the standard allowed your use case, then it would mean that the lifetime of the temporary would have to be extended all the way until the last reference to that variable. Now consider this simple extension of your example:
B* f() {
B * bp = new B(A());
return b;
}
void test() {
B* p = f();
delete p;
}
Now the problem is that the temporary (lets call it _T) is bound in f(), it behaves like a local variable there. The reference is bound inside *bp. Now that object's lifetime extends beyond the function that created the temporary, but because _T was not dynamically allocated that is impossible.
You can try and reason the effort that would be required to extend the lifetime of the temporary in this example, and the answer is that it cannot be done without some form of GC.
No, the extended lifetime is not further extended by passing the reference on.
In the second case, the temporary is bound to the parameter a, and destroyed at the end of the parameter's lifetime - the end of the constructor.
The standard explicitly says:
A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits.
§12.2/5 says “The second context [when the lifetime of a temporary
is extended] is when a reference is bound to a temporary.” Taken
literally, this clearly says that the lifetime should be extended in
your case; your B::a is certainly bound to a temporary. (A reference
binds to an object, and I don't see any other object it could possibly
be bound to.) This is very poor wording, however; I'm sure that what is
meant is “The second context is when a temporary is used to
initialize a reference,” and the extended lifetime corresponds to
that of the reference initiailized with the rvalue expression creating
the temporary, and not to that of any other references which may later
be bound to the object. As it stands, the wording requires something
that simply isn't implementable: consider:
void f(A const& a)
{
static A const& localA = a;
}
called with:
f(A());
Where should the compiler put A() (given that it generally cannot see
the code of f(), and doesn't know about the local static when
generating the call)?
I think, actually, that this is worth a DR.
I might add that there is text which strongly suggests that my
interpretation of the intent is correct. Imagine that you had a second
constructor for B:
B::B() : a(A()) {}
In this case, B::a would be directly initialized with a temporary; the
lifetime of this temporary should be extended even by my interpretation.
However, the standard makes a specific exception for this case; such a
temporary only persists until the constructor exits (which again would
leave you with a dangling reference). This exception provides a very
strong indication that the authors of the standard didn't intend for
member references in a class to extend the lifetime of any temporaries
they are bound to; again, the motivation is implementability. Imagine
that instead of
B b((A()));
you'd written:
B* b = new B(A());
Where should the compiler put the temporary A() so that it's lifetime
would be that of the dynamically allocated B?
Your example doesn't perform nested lifetime extension
In the constructor
B(const A & a_) : a(a_) { std::cout << " B()" << std::endl; }
The a_ here (renamed for exposition) is not a temporary. Whether an expression is a temporary is a syntactic property of the expression, and an id-expression is never a temporary. So no lifetime extension occurs here.
Here's a case where lifetime-extension would occur:
B() : a(A()) { std::cout << " B()" << std::endl; }
However, because the reference is initialized in a ctor-initializer, the lifetime is only extended until the end of the function. Per [class.temporary]p5:
A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits.
In the call to the constructor
B b((A())); //extra braces are needed!
Here, we are binding a reference to a temporary. [class.temporary]p5 says:
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.
Therefore the A temporary is destroyed at the end of the statement. This happens before the B variable is destroyed at the end of the block, explaining your logging output.
Other cases do perform nested lifetime extension
Aggregate variable initialization
Aggregate initialization of a struct with a reference member can lifetime-extend:
struct X {
const A &a;
};
X x = { A() };
In this case, the A temporary is bound directly to a reference, and so the temporary is lifetime-extended to the lifetime of x.a, which is the same as the lifetime of x. (Warning: until recently, very few compilers got this right).
Aggregate temporary initialization
In C++11, you can use aggregate initialization to initialize a temporary, and thus get recursive lifetime extension:
struct A {
A() { std::cout << " A()" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
};
struct B {
const A &a;
~B() { std::cout << "~B()" << std::endl; }
};
int main() {
const B &b = B { A() };
std::cout << "-----" << std::endl;
}
With trunk Clang or g++, this produces the following output:
A()
-----
~B()
~A()
Note that both the A temporary and the B temporary are lifetime-extended. Because the construction of the A temporary completes first, it is destroyed last.
In std::initializer_list<T> initialization
C++11's std::initializer_list<T> performs lifetime-extension as if by binding a reference to the underlying array. Therefore we can perform nested lifetime extension using std::initializer_list. However, compiler bugs are common in this area:
struct C {
std::initializer_list<B> b;
~C() { std::cout << "~C()" << std::endl; }
};
int main() {
const C &c = C{ { { A() }, { A() } } };
std::cout << "-----" << std::endl;
}
Produces with Clang trunk:
A()
A()
-----
~C()
~B()
~B()
~A()
~A()
and with g++ trunk:
A()
A()
~A()
~A()
-----
~C()
~B()
~B()
These are both wrong; the correct output is:
A()
A()
-----
~C()
~B()
~A()
~B()
~A()
In your first run, the objects are destroyed in the order they were pushed on the stack -> that is push A, push B, pop B, pop A.
In the second run, A's lifetime ends with the construction of b. Therefore, it creates A, it creates B from A, A's lifetime finishes so it is destroyed, and then B is destroyed. Makes sense...
I don't know about standards, but can discuss some facts which I saw in few previous questions.
The 1st output is as is for obvious reasons that a and b are in the same scope. Also a is destroyed after b because it's constructed before b.
I assume that you should be more interested in 2nd output. Before I start, we should note that following kind of object creations (stand alone temporaries):
{
A();
}
last only till the next ; and not for the block surrounding it. Demo. In your 2nd case, when you do,
B b((A()));
thus A() is destroyed as soon as the B() object creation finishes. Since, const reference can be bind to temporary, this will not give compilation error. However it will surely result in logical error if you try to access B::a, which is now bound to already out of scope variable.
§12.2/5 says
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.
Pretty cut and dried, really.

is twice calls to copy constructor happen for this c++ code sample?

for method:
Object test(){
Object str("123");
return str;
}
then, I had two methods to call it:
code 1:
const Object &object=test();
code 2:
Object object=test();
which one is better? is twice calls to copy constructor happen in code 2 if without optimize?
other what's the difference?
for code2 I suppose:
Object tmp=test();
Object object=tmp;
for code1 I suppose:
Object tmp=test();
Object &object=tmp;
but the tmp will be deconstructor after the method.so it must add const?
is code 1 right without any issues?
Let's analyse your function:
Object test()
{
Object temp("123");
return temp;
}
Here you're constructing a local variable named temp and returning it from the function. The return type of test() is Object meaning you're returning by value. Returning local variables by value is a good thing because it allows a special optimization technique called Return Value Optimization (RVO) to take place. What happens is that instead of invoking a call to the copy or move constructor, the compiler will elide that call and directly construct the initializer into the address of the caller. In this case, because temp has a name (is an lvalue), we call it N(amed)RVO.
Assuming optimizations take place, no copy or move has been performed yet. This is how you would call the function from main:
int main()
{
Object obj = test();
}
That first line in main seems to be of particular concern to you because you believe that the temporary will be destroyed by the end of the full expression. I'm assuming it is a cause for concern because you believe obj will not be assigned to a valid object and that initializing it with a reference to const is a way to keep it alive.
You are right about two things:
The temporary will be destroyed at the end of the full expression
Initializing it with a reference to const will extend its life time
But the fact that the temporary will be destroyed is not a cause for concern. Because the initializer is an rvalue, its contents can be moved from.
Object obj = test(); // move is allowed here
Factoring in copy-elision, the compiler will elide the call to the copy or move constructor. Therefore, obj will be initialized "as if" the copy or move constructor was called. So because of these compiler optimizations, we have very little reason to fear multiple copies.
But what if we entertain your other examples? What if instead we had qualified obj as:
Object const& obj = test();
test() returns a prvalue of type Object. This prvalue would normally be destructed at the end of the full expression in which it is contained, but because it is being initialized to a reference to const, its lifetime is extended to that of the reference.
What are the differences between this example and the previous one?:
You cannot modify the state of obj
It inhibits move semantics
The first bullet point is obvious but not the second if you are unfamiliar with move semantics. Because obj is a reference to const, it cannot be moved from and the compiler cannot take advantage of useful optimizations. Assigning reference to const to an rvalue is only helpful in a narrow set of circumstances (as DaBrain has pointed out). It is instead preferable that you exercise value-semantics and create value-typed objects when it makes sense.
Moreover, you don't even need the function test(), you can simply create the object:
Object obj("123");
but if you do need test(), you can take advantage of type deduction and use auto:
auto obj = test();
Your last example deals with an lvalue-reference:
[..] but the tmp will be destructed after the method. So must we add const?
Object &object = tmp;
The destructor of tmp is not called after the method. Taking in to account what I said above, the temporary to which tmp is being initialized will be moved into tmp (or it will be elided). tmp itself doesn't destruct until it goes out of scope. So no, there is no need to use const.
But a reference is good if you want to refer to tmp through some other variable. Otherwise, if you know you will not need tmp afterwards, you can move from it:
Object object = std::move(tmp);
Both your examples are valid - in 1 const reference refers to a temporary object, but lifetime of this object is prolonged till the reference goes out of scope (see http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/). The second example is obviously valid, and most modern compilers will optimize away additional copying (even better if you use C+11 move semantics) so for practical purposes examples are equivalent (though in 2 additionally you can modify the value).
In C++11, std::string has a move constructor / move assignment operator, hence the code:
string str = test();
will (at worst) have one constructor call and one move assignment call.
Even without move semantics, this will (likely) be optimised away by NRVO (return value optimisation).
Don't be afraid of returning by value, basically.
Edit: Just to make it 100% clear what is going on:
#include <iostream>
#include <string>
class object
{
std::string s;
public:
object(const char* c)
: s(c)
{
std::cout << "Constructor\n";
}
~object()
{
std::cout << "Destructor\n";
}
object(const object& rhs)
: s(rhs.s)
{
std::cout << "Copy Constructor\n";
}
object& operator=(const object& rhs)
{
std::cout << "Copy Assignment\n";
s = rhs.s;
return *this;
}
object& operator=(object&& rhs)
{
std::cout << "Move Assignment\n";
s = std::move(rhs.s);
return *this;
}
object(object&& rhs)
: s(std::move(rhs.s))
{
std::cout << "Move Constructor\n";
}
};
object test()
{
object o("123");
return o;
}
int main()
{
object o = test();
//const object& o = test();
}
You can see that there is 1 constructor call and 1 destructor call for each - NRVO kicks in here (as expected) eliding the copy/move.
Code 1 is correct. As I said, the C++ Standard guarantees a temporary to a const reference is valid. It's main usage is polymorphic behavior with refenences:
#include <iostream>
class Base { public: virtual void Do() const { std::cout << "Base"; } };
class Derived : public Base { public: virtual void Do() const { std::cout << "Derived"; } };
Derived Factory() { return Derived(); }
int main(int argc, char **argv)
{
const Base &ref = Factory();
ref.Do();
return 0;
}
This will return "Derived". A famouse example was Andrei Alexandrescu's ScopeGuard but with C++11 it's even simpler yet.

Lifetime of temporaries [duplicate]

This question already has answers here:
Does a const reference class member prolong the life of a temporary?
(6 answers)
Closed 9 years ago.
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.
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.
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.
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.

C++ const lvalue references

Assuming I have:
class A which is non-copyable
class B which has as a member, const A& a (and takes an A in its constructer and sets it in its initialization list)
a function A GenerateA();
Does this mean that it should be valid to do:
B(GenerateA())
?
i.e, does the const ref mean that no copy of the A that generateA() returns is done? And does that mean that the scope of the returned temporary is extended for as long as B exists?
EDIT: Addon question from the comments:
Is it acceptable to return a A& from GenerateA() to a local A, if the lvalue is a const A&?
Thanks!
If A is non-copyable, then the function A GenerateA() is invalid since returning by value requires creating a copy.
If the function returns a reference instead (i.e. A &GenerateA()) and the reference is to a locally created A object, it becomes invalid as soon as the function exits. C++ doesn't have any form of garbage collection, so there is no way to "extend" the lifetime of an object as long as it is in use.
As it has already been stated by others, A GenerateA() cannot compile if A is not copyable.
Regarding the const ref : no, the lifetime of the temporary will not be extended to the lifetime of B. The standard [12.2.5] states :
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 the returned value in a function return statement (6.6.3) persists until the function exits.
So yes, extension of the lifetime of a temporary exists in some contexts (and is sometime truly useful : see this article), but not in the one you presented.
Regarding your last question, it's not legal to return a reference to a local variable from GenerateA() (and binding the result to a const reference won't be of any help).
Yes and No.
Yes, the const reference will bind to the temporary variable. No, const references which are class members do not extend lifetime the way const references with automatic duration do.
Here's an example:
#include <iostream>
using namespace std;
int& GenX(bool reset)
{
static int* x = new int;
*x = 100;
if (reset)
{
delete x;
x = new int;
*x = 200;
}
return *x;
}
class YStore
{
public:
YStore(int& x);
int& getX() { return my_x; }
private:
int& my_x;
};
YStore::YStore(int& x)
: my_x(x)
{
}
int main()
{
YStore Y(GenX(false));
cout << "X: " << Y.getX() << endl;
GenX(true); // side-effect in Y
cout << "X: " << Y.getX() << endl;
return 0;
}
Output:
X: 100
X: 200

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?
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
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.
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.
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