Does this const reference have its life preserved? - c++

I have found this answer to the question "Does a const reference prolong the life of a temporary?", which states:
Only local const references prolong the lifespan.
I'm afraid my standardese is not up to scratch to know whether foo, below, is a local const reference or not.
Does my const std::string& foo below prolong the lifetime of the temporary std::string function argument created in the call to get_or, or do I have a dangling reference?
#include <iostream>
#include <boost/optional.hpp>
struct Foo
{
const std::string& get_or(const std::string& def)
{
return str ? str.get() : def;
}
boost::optional<std::string> str;
};
int main()
{
Foo f;
const std::string& foo = f.get_or("hello world");
std::cout << foo << '\n';
}

const& won't extend lifetimes in that situation. Consider the example here that constructs a temporary and then attempts to print it: it's using the same constructs as your code, but I've altered it to make object construction and destruction more explicit to the user.
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "Printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = get_or(reporting{});
foo.print();
return 0;
}
Output:
Constructed
Destructed
printing.
Note how the object is destroyed before printing. is displayed.
You might be wondering why the code still completes with no visible errors: it's the result of Undefined Behavior. The object in question doesn't exist, but because it doesn't depend on state to invoke its method, the program happens to not crash. Other, more complicated examples should carry no guarantee that this will work without crashing or causing other, unexpected behavior.
Incidentally, things are a little different if the temporary is bound directly to the const&:
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = reporting{};
foo.print();
return 0;
}
Output:
Constructed
printing.
Destructed
See how the object isn't destroyed until after it is used. In this situation, the object survives until the end of scope.

You passed the string through too many references.
Binding the temporary string to the def parameter of get_or extends the lifetime of the string to the end of the full expression containing the function call, but binding def to the return value of get_or and binding the return value of get_or to foo do not extend the lifetime further. The string is dead by the time you try to print it.

The "temporary" in question is the std::string-object created when calling get_or with a parameter of type const char*. The lifetime of this temporary object is limited with the end of function get_or, and the fact that you return a reference to this temporary and assign it afterwards does not prolong the lifetime. See the following code which uses a simple "custom" string class, which couts construction and destruction:
class MyString {
public:
MyString (const char* str) {
m_str = strdup(str);
cout << "constructor MyString - '" << m_str << "'" << endl;
}
~MyString() {
cout << "destructor MyString - '" << m_str << "'" << endl;
free(m_str);
}
char *m_str;
};
struct Foo
{
const MyString& get_or(const MyString& def)
{
cout << "Foo::get_or with '" << def.m_str << "'" << endl;
return def;
}
};
int main()
{
Foo f;
const MyString& foo = f.get_or("hello world");
cout << "usage of foo?" << endl;
}
Output:
constructor MyString - 'hello world'
Foo::get_or with 'hello world'
destructor MyString - 'hello world'
usage of foo?
Note that the destructor is called before you will have the chance to use foo.
The situation is different if you assign a reference to a temporary directly. Again, the lifetime is until the end of the function main, but it will be used in main and not in any function calling main:
const MyString& foo2 = MyString("hello world2");
cout << "usage of foo..." << endl;
Then the output will be:
constructor MyString - 'hello world2'
usage of foo...
destructor MyString - 'hello world2'

Related

Should we use lifetime extension of a temporary in C++17 and later?

Since in C++17 it's guaranteed that the temporary created by an expression is stored in a variable assigned to:
#include <iostream>
struct Test
{
Test() { std::cout << "Test()" << std::endl; }
Test(const Test &rhs) { std::cout << "Test(const Test &rhs)" << std::endl; }
Test(Test &&rhs) { std::cout << "Test(Test &&rhs)" << std::endl; }
Test &operator=(Test &&rhs) { std::cout << "Test &operator=(Test &&rhs)" << std::endl; return *this; }
Test &operator=(const Test &rhs) { std::cout << "Test &operator=(const Test &rhs)" << std::endl; return *this; }
~Test() { std::cout << "~Test()" << std::endl; }
};
Test fun()
{
return Test{};
}
int main(int argc, char** argv)
{
auto t = fun();
return 0;
}
Outputs:
Test()
~Test()
Deleting the assignment operators and the copy and move constructors yields the same result.
Do we still need to extend the lifetime of the temporary ('const auto &t = fun()') for any kind of optimization?
EDIT:
Test &operator=(const Test &&rhs) { std::cout << "Test &operator=(const Test &rhs)" << std::endl; return *this; }
Is now:
Test &operator=(const Test &rhs) { std::cout << "Test &operator=(const Test &rhs)" << std::endl; return *this; }
EDIT:
Question clarified.
EDIT:
Removed the 'language-lawyer' tag. This is a genuine question that affects much of my codebase. People normally use lifetime-extension of temporaries for performance reasons. But writing 'const auto &p = ...' is longer than just write 'auto p = ...', which is cleaner and expresses more the desire of the programmer.
Yes, I would still like this to work:
auto const& t = fun();
And for that to work relies upon extending the lifetime of the temporary fun() returns to match the lifetime of t. Otherwise, the Test temporary would be destroyed at end of the expression and I'd immediately have a dangling reference.
You need some way to say "give me whatever" that avoids work if the "whatever" gives you an lvalue. I don't want to do auto t = fun(); where fun() returns a T const&, that's an unnecessary copy. auto const& (or auto&&) avoids the copy in this case, and with lifetime extension works with the prvalue case as well.

Reference to a temporary vs Pointer to temporary and their lifetime

I have read many articles regarding the lifetime of a temporary and it seems the lifetime of temporary object is extended in certain cases, while in the rest of the cases, its a dangling object.
In my case the temporary object is returned from a function, I want to understand if the const ref to the temporary object will remain valid or not.
Here is the code:
class MyClass
{
public:
std::vector<int> vec{ 1, 2 };
MyClass()
{
cout << "Ctor" << endl;
}
MyClass(const MyClass &copy)
{
vec = copy.vec;
cout << "Copy Ctor" << endl;
}
~MyClass()
{
cout << "Dtor" << endl;
}
};
MyClass access()
{
MyClass obj;
obj.vec[0] = 10;
return obj;
}
int main()
{
{
const auto &ret = access(); // calls the copy-ctor and assigns the temporary to reference 'ret'
auto val = ret.vec[0];
cout << "By Ref = " << val << endl; // works fine
}
cout << "_____________________________________" << endl;
{
const auto *ret = &access(); // temporary is lost
auto val = ret->vec[0]; // program crash
cout << "By Pointer = " << val << endl;
}
return 0;
}
Only temporaries bound to const references have their lifetime extended, with the exception of const references bound to return of functions, such as
const int& f(){int x{42}; return x;}
int main()
{
const int& bad = f(); // you'll end up with a dangling reference
}
or
const int& f(const int& x)
{
return x;
}
int main(){
{
const int& bad = f(42); // you'll end up (again) with a dangling reference
}
In your second case you have a pointer, so the lifetime of the right hand side temporary is not prolonged.

C++11 move to local const reference: scope [duplicate]

This question already has answers here:
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
For regular local const reference variables, the scope is prolonged. Which is why the following code works as expected:
#include <iostream>
#include <memory>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
auto const& f = std::make_shared<foo>();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints:
// foo() #0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() #0x55f249c58e80
It seems though that this does not work as expected when assigning a moved object using std::move():
#include <iostream>
#include <memory>
#include <list>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
std::list<std::shared_ptr<foo>> l;
l.push_back(std::make_shared<foo>());
auto const& f = std::move(l.front());
l.clear();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints
// foo() #0x564edb58fe80
// ~foo() #0x564edb58fe80
// f = 0x564edb58fe80
Does std::move() indeed change the scope, or am I dealing with a compiler bug?
Changing the variable from auto const& f to just auto f fixes the problem. If I wrap the move into another function, it also works:
auto const& f = [&]() { return std::move(l.front()); }();
It's almost like std::move() does not share the same semantics as a function call, but rather as if it was just a regular variable assignment:
auto const& f = std::move(l.front());
Let's put aside std::move() and create this function:
struct sometype {};
const sometype &foobar( const sometype &cref ) { return cref; }
and now we use it:
const sometype &ref = foobar( sometype() );
What do you think, will lifetime of temporary be prolongated in this case? No it would not. Lifetime is only prolongated when you assign to a reference directly. When it goes through a function or through static_cast or std::move that prolongation is gone. So you have exactly the same issue with std::move()
struct sometype {
sometype() { std::cout << "ctype()" << std::endl; }
~sometype() { std::cout << "~ctype()" << std::endl; }
};
const sometype &foobar( const sometype &cref ) { return cref; }
int main()
{
const sometype &cref1 = foobar( sometype() );
sometype &&cref2 = std::move( sometype() );
std::cout << "main continues" << std::endl;
}
output:
ctype()
~ctype()
ctype()
~ctype()
main continues
live example
Note: you should not use std::move() on return statement, it does not give you anything.
For your code change. You should remember that std::move() does not move anything. It is done by special assignment operator or constructor (if they provided for a type). So when you write this code:
const type &ref = std::move( something );
there is no constructor nor assignment operator involved and so no moving happens. For actual moving to happen you have to assign it to a variable:
type val = std::move( something );
now moving would happen if possible or copy otherwise.

common practice for member and parameter storage

seeking general guidance on common design practice here in > c++14 world.
Is const std::string& mName the most appropriate type here? it has the least copies, but is it good style?
I feel like a ref implies some other alias that is or could be in unknown state at some point.
Is it best style for one to make the defensive copy? and go const std::string mName instead? or if that is the case take the copy in the parameter const std::string aName and keep the & in the member field?
whats the most common practice for parameter and member storage and what are the factors that push the decision in other ways?
class Machine {
private:
const std::string& mName;
public:
Machine(const std::string& aName) : mName(aName) {
std::cout << &aName << std::endl;
std::cout << &mName << std::endl;
std::cout << std::endl;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
EDIT *
This feels better and also eliminate extra copy on temporaries I think??
class Machine {
private:
const std::string mName;
public:
Machine(const std::string&& aName) : mName(aName) {
std::cout << "1:: " << &aName << std::endl;
std::cout << "1:: " << &mName << std::endl;
}
Machine(const std::string& aName) : mName(aName) {
std::cout << "2:: " << &aName << std::endl;
std::cout << "2:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
If you're going to store the constructor parameter in your object, then take the object by value. If the caller passes an rvalue, it will be move constructed in. If the user passes an lvalue, you were going to have to create a copy when you copy it into your object, anyhow, so just do it a step earlier.
This results in the cleanest, easiest to understand code:
class Machine {
private:
const std::string mName;
public:
Machine(const std::string aName) : mName(std::move(aName)) {
std::cout << "1:: " << &aName << std::endl; // this has been moved out of...
std::cout << "1:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
Machine m(function_that_returns_a_string()); // move constructed all the way in - no copies made
string s;
Machine m2(s); // only one copy is made - when calling constructor, but it's then move constructed into the object
}
There are times when objects are still too expensive to move construct, at which point making two versions taking an rvalue and lvalue reference will be a win, but for most cases, the style above is the best approach.

const value and RVO

Say I have this function:
template <class A>
inline A f()
{
A const r(/* a very complex and expensive construction */);
return r;
}
Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.
As demonstrated here, NRVO elides the copy of r implied by the line return r;
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
And the copy in main is also elided.
If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
//A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.
Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.
If class A is under your control, and you want to return const objects by move, you can do
mutable bool resources_were_stolen = false;
and set that to true in a const move constructor
A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }
Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.