Reputation: 43
I have a scenario where I need to convert a function that can be chained by *this
to returning std::optional<std::reference_wrapper<T>>
instead of T&
(reason is out of scope for this question). The reason why I use std::reference_wrapper
is since std::optional
cannot take a reference, at least not in C++11. However, that doesn't work because I seem to be encountering lifetime issues. Here is a minimal example:
#include <iostream>
#include <functional>
struct test {
std::reference_wrapper<test> foo() {
val = 42;
return *this;
}
test& foo2() {
val = 50;
return *this;
}
int val;
};
void bar(test t) {
std::cout << std::move(t).val << "\n";
}
int main()
{
auto f = test().foo();
bar(f);
auto g = test().foo2();
bar(g);
}
This outputs 0 50
instead of the expected 42 50
. If I split it up into two statements:
auto f = test();
auto f2 = f.foo();
bar(f2);
It works as expected. Using the debugger, I discover that the compiler is optimizing some of the expression away and val
is left uninitialized, which leads me to think I have undefined behavior in my code.
Do I have undefined behavior? If so, how do I avoid it here?
Upvotes: 4
Views: 254
Reputation: 170104
Do I have undefined behavior?
Yes. auto
deduces the type of the object from the expression used to initialize it. And you use an expression of type std::reference_wrapper<test>
to initialize f
. The temporary test()
is gone after f
is initialized, so f
dangles immediately.
You can either split the declaration as you do already, or use std::references_wrappers
's get member function:
auto f = test().foo().get();
Either way, std::reference_wrapper<test>
is not a drop in replacement for a reference in all contexts C++ supports. Proxy objects never are.
Upvotes: 5
Reputation: 38287
Do I have undefined behavior?
Yes. Have a look at the line auto f = test().foo();
. f
is a std::reference_wrapper<test>
and the test
instance it refers to is test()
. The lifetime of test()
ends right at the end of this line, and you end up with a dangling reference. That's not the case for auto g = test().foo2();
as it copies the return value (thanks to @StoryTeller for helping me out here).
how do I avoid it here?
You need to tear apart lifetime management from the std::reference_wrapper
part. This will work:
test t;
auto f = t.foo();
// Do stuff with f until this scope ends.
Upvotes: 3