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?
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.
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