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
Related
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.
Why does this:
#include <string>
#include <iostream>
using namespace std;
class Sandbox
{
public:
Sandbox(const string& n) : member(n) {}
const string& member;
};
int main()
{
Sandbox sandbox(string("four"));
cout << "The answer is: " << sandbox.member << endl;
return 0;
}
Give output of:
The answer is:
Instead of:
The answer is: four
Only local const references prolong the lifespan.
The standard specifies such behavior in §8.5.3/5, [dcl.init.ref], the section on initializers of reference declarations. The reference in your example is bound to the constructor's argument n, and becomes invalid when the object n is bound to goes out of scope.
The lifetime extension is not transitive through a function argument. §12.2/5 [class.temporary]:
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 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 [class.base.init]) persists until the constructor exits. A temporary bound to a reference parameter in a function call (§5.2.2 [expr.call]) persists until the completion of the full expression containing the call.
Here's the simplest way to explain what happened:
In main() you created a string and passed it into the constructor. This string instance only existed within the constructor. Inside the constructor, you assigned member to point directly to this instance. When when scope left the constructor, the string instance was destroyed, and member then pointed to a string object that no longer existed. Having Sandbox.member point to a reference outside its scope will not hold those external instances in scope.
If you want to fix your program to display the behavior you desire, make the following changes:
int main()
{
string temp = string("four");
Sandbox sandbox(temp);
cout << sandbox.member << endl;
return 0;
}
Now temp will pass out of scope at the end of main() instead of at the end of the constructor. However, this is bad practice. Your member variable should never be a reference to a variable that exists outside of the instance. In practice, you never know when that variable will go out of scope.
What I recommend is to define Sandbox.member as a const string member; This will copy the temporary parameter's data into the member variable instead of assigning the member variable as the temporary parameter itself.
Technically speaking, this program isn't required to actually output anything to standard output (which is a buffered stream to begin with).
The cout << "The answer is: " bit will emit "The answer is: " into the buffer of stdout.
Then the << sandbox.member bit will supply the dangling reference into operator << (ostream &, const std::string &), which invokes undefined behavior.
Because of this, nothing is guaranteed to happen. The program may work seemingly fine or may crash without even flushing stdout -- meaning the text "The answer is: " would not get to appear on your screen.
It's clear from the other answers that class members don't prolong the life of a temporary beyond the constructor call. There are cases though were your API can "safely" assume that all const& objects passed to a class won't be temporaries, but references to well scoped objects.
If you don't want to create copies, what can you do to ensure UB doesn't creep into your code? The best tool you have is to safeguard the assumption that std::string const& passed to the constructor are not temporaries, by declaring as deleted the overload that accepts such temporaries:
#include <string>
#include <iostream>
using namespace std;
class Sandbox
{
public:
Sandbox(const string& n) : member(n) {}
Sandbox(string&&) = delete;
// ^^^ This guy ;)
const string& member;
};
int main()
{
Sandbox sandbox(string("four"));
// Detect you're trying ^^^ to bind a
// reference to a temporary and refuse to compile
return 0;
}
Demo
Because your temporary string went out of scope once the Sandbox constructor returned, and the stack occupied by it was reclaimed for some other purposes.
Generally, you should never retain references long-term. References are good for arguments or local variables, never class members.
you're referring to something which has vanished. The following will work
#include <string>
#include <iostream>
class Sandbox
{
public:
const string member = " "; //default to whatever is the requirement
Sandbox(const string& n) : member(n) {}//a copy is made
};
int main()
{
Sandbox sandbox(string("four"));
std::cout << "The answer is: " << sandbox.member << std::endl;
return 0;
}
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.
Class A
{
A(int& foo) : m_foo(foo) {}
int& m_foo;
};
int main(void)
{
A* bar = 0;
{
int var = 5;
bar = new A(var);
}
std::cout << "Is m_foo still valid?:" << bar.m_foo << std::endl;
}
"m_foo" is a reference and "var" is a local variable which is given to the constructor. "var" gets out of the scope before printing the value so does it make m_foo also invalid?
If m_foo is a pointer, then it would be invalid but does it work the same way with references?
m_foo is not valid when int var falls out of scope. The thing to which it refers has gone away.
Yes, a reference member becomes invalid if referenced object gets de-allocated. Same as with pointers. If you intend to keep references, make sure the lifetimes nest. Or use something like boost::weak_ptr.
Yes. The behaviour is undefined.
"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