I was binding some return value of a function to a const lvalue reference, but the object was deleted, before the lifetime of the const lvalue reference ended.
In the following example the Foo object is destroyed before the lifetime of foo
ends:
#include <iostream>
#include <string>
struct Foo
{
~Foo()
{
std::cout << "Foo destroyed: " << name << std::endl;
}
std::string name;
};
Foo&& pass_through(Foo&& foo)
{
return std::move(foo);
}
int main()
{
const Foo& foo = pass_through({"some string"});
std::cout << "before scope end" << std::endl;
}
The output is:
Foo destroyed: some string
before scope end
live on coliru: 1
I thought you can bind const T&
to anything. Is it bad practice to return T&&
and should returning by value be preferred?
I stumbled across this in the cpprestsdk here:
inline utility::string_t&& to_string_t(std::string &&s) { return std::move(s); }
https://github.com/Microsoft/cpprestsdk/blob/master/Release/include/cpprest/asyncrt_utils.h#L109
Very confusing, because the windows version of to_string_t
(dispatched by preprocessor macros) returned by value:
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s);
Edit:
Why does it work when passing the result of pass_through
to a function taking const Foo&
? The lifetime is extended in this case?
An lvalue reference can bind to an lvalue, but not to an rvalue.
Such a reference is called an lvalue reference to a const value (sometimes called a reference to const or a const reference). In the above program, we bind const reference ref to modifiable lvalue x . We can then use ref to access x , but because ref is const, we can not modify the value of x through ref .
From the standard:
15.2 Temporary objects
6.9 A temporary object bound to a reference parameter in a function call persists until the completion of the full-expression containing the call.
Essentially what it's saying is that because you passed in a temporary object and then didn't extend its lifetime (say, by moving it into an lvalue) then its lifetime only lasts until the first ;
after the call to pass_through
in your code. After this point you are left with foo
as a dangling reference.
int main()
{
const Foo& foo = pass_through({"some string"}); // "some string" lifetime ends here
std::cout << "before scope end" << std::endl;
}
As to whether it is good practice to to return an rvalue reference I believe these two answers already go into detail on the subject:
Should I return an rvalue reference parameter by rvalue reference?
Is returning by rvalue reference more efficient?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With