Const reference as class member - c++

Even if the subject was discussed many times around here, I can't find a conclusive explanation regarding my particular case. Will const extend the lifetime of the RefTest temporary? Is the below example legal?
#include <iostream>
class RefTest
{
public:
RefTest(const std::string &input) : str(input) {}
~RefTest () {std::cout << "RefTest" << std::endl;}
private:
std::string str;
};
class Child
{
public:
Child (const RefTest &ref) : ref_m(ref) {}
~Child () {std::cout << "Test" << std::endl;}
private:
const RefTest &ref_m;
};
class Test
{
public:
Test () : child(RefTest("child")) {}//Will the temporary get destroyed here?
~Test () {std::cout << "Test" << std::endl;}
private:
const Child child;
};
int main ()
{
Test test;
}

The reference does not extend the lifetime. The code is legal, but only because you never access ref_m after the constructor finishes.
The temporary is bound to the constructor parameter, ref. Binding another reference to it later, ref_m, doesn't extend the lifetime. If it did, you'd have an object on the stack which has to persist as long as the reference member it's bound to, which could be allocated on the heap, so the compiler would be unable to unwind the stack when the constructor returns.
It would be nice to get a warning, but compilers aren't perfect and some things are difficult to warn about. The temporary is created in a different context from where it's bound to a reference, so the compiler can only tell there's a problem with inlinging turned on, or some clever static analysis.

The C++ standard states:
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) 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.
NOTE: And by the way, this is duplicate (1, 2), you should search better, next time... :)

Related

Calling a const reference to an Rvalue of a class with virtual functions causes exception [duplicate]

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;
}

How to use object that created locally in a function without copying it in memory?

For example I have a class that call a function in its consturctor that returns local object. I'm trying to use rvalue references to get access to this object to avoid expensive move of it in memory.
class MyClass
{
BigObject&& C;
MyClass() : C(f())
{
};
};
BigObject f()
{
return BigObject();
}
But compiller tells me that reference member is initialized to a temporary that doesn't persist after the construction exits.
I don't get it. I understand that local objects, created in a scope of a function, exists only in a scope of function. Reaching the end of the scope - destructors of local objects are called. And here I initialize rvalue reference with local object , and I have access to it, while I'm in the body of constuctor.
Can someone explain, what is going on here? And is there a way to return a local object and use it as any ligetable class member, without moving it in memory?
You should remove the && from C.
The code is not as expensive as you imagine: f's return value is a copy elision context. So the compiler is allowed to construct just a single BigObject directly in the memory space for C. Even if a compiler doesn't perform this , it is still a move context so as worst case scenario the object will be moved.
If somehow your object is copyable but not movable then you do have to rely on copy elision but it's hard to imagine a valid use case for such an object.
To quote it from the standard,
Section 12.2.5 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 of a subobject to which the
reference is bound persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s
ctor-initializer (12.6.2) persists until the constructor exits.
So, in your case 'C(f())', C gets bound to the temporary only till the constructor exits and after that it's kind of UB and is upto your luck. You should be thankful that the compiler reported a warning :).
Anyhow, there is no need to do such acrobatics, but you can always move from the temporary into 'C'.
class BigObject
{
public:
BigObject() {std::cout << "Cons" << std::endl;}
BigObject(const BigObject& other) {std::cout << "Copy Cons" << std::endl;}
BigObject(BigObject&& other) {std::cout << "Move Cons" << std::endl;}
};
BigObject f()
{
return BigObject();
}
class MyClass
{
public:
BigObject C;
MyClass() : C(f())
{
};
};
In this case compiler can very well elide the copy making the code as efficient as possible by just having to construct 'BigObject' once at the target site.
So, in this particular case it could be possible that your object is not moved at all or even copied. Perhaps that answers your final question.

Binding const& of temporary: No compiler warning?

I have a TestClass with a const& member variable. I know from various places and own experiences that it is a bad idea to initialize this const& with the reference to a temporary value. So I was quite suprised that the following code will compile fine (tested with gcc-4.9.1, clang-3.5, and scan-build-3.5) but fail to run properly.
class TestClass {
public:
// removing the "reference" would remove the temporary-problem
const std::string &d;
TestClass(const std::string &d)
: d(d) {
// "d" is a const-ref, cannot be changed at all... if it is assigned some
// temporary value it is mangled up...
}
};
int main() {
// NOTE: the variable "d" is a
// temporary, whose reference is not valid... what I don't get in the
// moment: why does no compiler warn me?
TestClass dut("d");
// and printing what we got:
std::cout << "beginning output:\n\n";
// this will silently abort the program (gcc-4.9.1) or be empty
// (clang-3.5) -- don't know whats going on here...
std::cout << "dut.d: '" << dut.d << "'\n";
std::cout << "\nthats it!\n";
return 0;
}
Why does none of the two compilers warn me at compile-time? See also this ideone, with some more testing going on.
No warning as no offense:
local const references prolong the lifespan of the variable.
The standard specifies such behavior in §8.5.3/5, [dcl.init.ref], the section on initializers of reference declarations. 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.
You can have a look at gotw-88 for an extended and more readable discussion on this topic.
Undefined Behaviour
So is it your code correct? Nope, and its execution will lead to undefined behaviour. The real problem in your code snapshot is that the Undefined Behaviour is caused by the mix of two perfectly legal operations: the call of the constructor passing a temporary object (whose life spans inside the constructor block) and the binding of the reference in the constructor definition.
The compiler is not smart enough to detect this explosive statement combination, so this is why you don't get any warning.
Binding a const & to a temporary is valid and the compiler will ensure that the temporary will live at least as long as the reference. This allows you to do things like pass string literals into functions expecting a const std::string &.
In your case however you are copying that reference and thus the lifetime guarantee no longer holds. Your constructor exits and the temporary is destroyed and you are left with a reference to invalid memory.
The problem is that there is no single point in which a warning would be warranted. It's only the combination of the call of the constructor and its implementation that leads to Undefined Behaviour.
If you consider just the constructor:
class TestClass {
public:
const std::string &d;
TestClass(const std::string &d)
: d(d)
{}
};
There's nothing wrong here, you got a reference and you're storing one. Here's an example of perfectly valid use:
class Widget {
std::string data;
TestClass test;
public:
Widget() : data("widget"), test(data)
{}
};
If you consider just the call site:
//Declaration visible is:
TestClass(const std::string &d);
int main() {
TestClass dut("d");
}
Here, the compiler doesn't "see" (in the general case) the definition of the constructor. Imagine an alternative:
struct Gadget {
std::string d;
Gadget(cosnt std::string &d) : d(d) {}
};
int main()
{
Gadget g("d");
}
Surely you wouldn't want a warning here either.
To summarise, both the call site and the constructor implementation are perfectly usable as-is. It's only their combination which causes issues, but that combination is beyond the context a compiler can reasonably use to emit warnings.
TestClass(const std::string &d1)
: d(d1) {
TestClass dut("d");
I guess following is happening logically:-
1) Your string literal ("d") would be implicitly converted to std::string ( Let's give it a name 'x' ).
2) So, 'x' is a temporary which is bound to d1 here. Lifetime of this temporary is extended to lifetime of your d1. Although that string literal would always be alive till the end of program.
3) Now you're making 'd' refer to 'd1'.
4) At the end of your constructor d1's lifetime is over and so is d's.
All compiler's are not so clever to figure out these minor glitches...

Why does std::reference_wrapper<const T> not accept a temporary?

Normally, rvalues can bind to const references (const SomeType&). It's built into the language. However, std::reference_wrapper<const T> does not accept an rvalue as its constructor argument since the corresponding overload is deliberately deleted. What is the reason for this inconsistency? std::reference_wrapper is "advertised" as the alternative to a reference variable for cases when we must pass by value but would like to preserve reference semantics.
In other words, if the rvalue to const & binding is considered safe, since it's built into the language, why did the designers of C++11 not allow rvalues to be wrapped in std::reference_wrapper<const T>?
When would this come handy, you may ask. For example:
class MyType{};
class Foo {
public:
Foo(const MyType& param){}
};
class MultiFoo {
public:
MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){}
};
int main()
{
Foo foo{MyType{}}; //ok
MultiFoo multiFoo{MyType{}, MyType{}}; //error
}
INTRODUCTION
Normally T const& and T&& can extend the lifetime of a temporary directly bound to it, but this is not applicable if the reference is "hiding" behind a constructor.
Since std::reference_wrapper is copyable (by intention), the handle to the referenced object can outlive the temporary if the std::reference_wrapper is used in such a way that the handle escapes the scope where the temporary is created.
This will lead to a lifetime mismatch between the handle and the referred to object (ie. the temporary).
LET'S PLAY "MAKE BELIEVE"
Imagine having the below, illegal, snippet; where we pretend that std::reference_wrapper has a constructor that would accept a temporary.
Let's also pretend that the temporary passed to the constructor will have its lifetime extended (even though this isn't the case, in real life it will be "dead" right after (1)).
typedef std::reference_wrapper<std::string const> string_ref;
string_ref get_ref () {
string_ref temp_ref { std::string { "temporary" } }; // (1)
return temp_ref;
}
int main () {
string_ref val = get_ref ();
val.get (); // the temporary has been deconstructed, this is dangling reference!
}
Since a temporary is created with automatic storage duration, it will be allocated on the storage bound to the scope inside get_ref.
When get_ref later returns, our temporary will be destroyed. This means that our val in main would refer to an invalid object, since the original object is no longer in existance.
The above is the reason why std::reference_wrapper's constructor doesn't have an overload that accepts temporaries.
ANOTHER EXAMPLE
struct A {
A (std::string const& r)
: ref (r)
{ }
std::string const& ref;
};
A foo { std::string { "temporary " } };
foo.ref = ...; // DANGLING REFERENCE!
The lifetime of std::string { "temporary" } will not be extended, as can be read in the standard.
12.2p5 Temporary objects [class.temporary]
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
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.
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.
Note: it's important to note that a constructor is nothing more than a "fancy" function, called upon object construction.
Binding a temporary directly to a reference prolongs its lifetime.
struct Foo {};
Foo f() { return {}; }
void g() {
f(); // temporary destroyed at end of full-expression
const Foo& r = f(); // temporary destroyed at end of scope
// r can still be used here ...
// ...
// all the way down to here
}
However, it must bind directly to the temporary. Consider the following example:
struct Bar {
Bar(const Foo& f): r(f) {}
const Foo& r;
};
void h() {
Bar b(f());
// binding occurs through parameter "f" rather than directly to temporary "f()"
// b.r is now a dangling reference! lifetime not extended
}
Which would make it quite useless to wrap a temporary, even if you could.
Note: an actual reference wrapper object uses a pointer, so that it can be reassigned:
struct Baz {
Baz(const Foo& f): p(std::addressof(f)) {}
Baz& operator=(const Foo& f) { p = std::addressof(f); return *this; }
const Foo* p;
};
The same still applies: the temporary passed into the constructor or assignment operator will be destroyed at the end of the full-expression containing the call.
You should not copy a const reference as it does not necessarily keep the referenced object alive. The following code shows the problem:
#include <iostream>
struct X { int x; };
struct Foo {
const X& a;
Foo(const X& na) : a(na) {} // here na is still OK
};
int main()
{
Foo foo{X{3}}; // the temporary exists only for this expression
// now the temporary is no longer alive and foo.a is a dangling reference
std::cout << foo.a.x << std::endl; // undefined behavior
}
The const reference keeps the temporary X{3} alive for the constructor of Foo, but not for the object itself. You get a dangling reference.
To protected you from this problem the usage of temporary objects with std::reference_wrapper and std::ref has been disabled.
Since a const T&& variable can nor be moved, neither modified, there's no reason for using it (there's no adventages or differences over a const T&). Even more, a posterior use of that reference can be dangerous if the corresponding temporary is no longer alive (actually, it's dangerous, because it invokes undefined behaviour in such a case).
The deletion of its rvalue-reference constructor is not a bad idea after all.

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.