*此周围的std :: reference_wrapper

时间:2019-03-07 10:02:00

标签: c++ c++11

我有一种情况,我需要将可以由*this链接的函数转换为返回std::optional<std::reference_wrapper<T>>而不是T&的原因(此问题的原因不在此范围内)。我之所以使用std::reference_wrapper是因为std::optional不能引用,至少在C ++ 11中不能。但是,这不起作用,因为我似乎遇到了终身问题。这是一个最小的示例:

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

这将输出0 50而不是预期的42 50。如果我将其分为两个语句:

auto f = test();
auto f2 = f.foo();
bar(f2);

它按预期工作。使用调试器,我发现编译器正在优化某些表达式,并且val未初始化,这使我认为我的代码中存在未定义的行为。

我有未定义的行为吗?如果是这样,我该如何避免呢?

2 个答案:

答案 0 :(得分:5)

  

我有未定义的行为吗?

是的。 auto从用于初始化对象的表达式中推断出对象的类型。然后,您使用类型为std::reference_wrapper<test>的表达式来初始化f。初始化test()之后,临时f消失了,因此f立即挂了。

您可以像现在一样拆分声明,也可以使用std::references_wrappers的get成员函数:

auto f = test().foo().get();

无论哪种方式,std::reference_wrapper<test>都不是C ++支持的所有上下文中的引用的替代品。代理对象永远不会。

答案 1 :(得分:3)

  

我有未定义的行为吗?

是的。看一下auto f = test().foo();行。 fstd::reference_wrapper<test>,它引用的test实例是test()test()的生存期恰好在此行的结尾处结束,您最终得到了一个悬空的引用。 auto g = test().foo2();并非如此,因为它复制了返回值(感谢@StoryTeller,在这里为我提供了帮助)。

  

如何在这里避免呢?

您需要从std::reference_wrapper部分中删除生命周期管理。这将起作用:

test t;
auto f = t.foo();

// Do stuff with f until this scope ends.