c++ - 为什么这段代码涉及使用对临时段错误的引用,尽管它似乎正确地管理了生命周期?

标签 c++ c++14 undefined-behavior lifetime

在试验免移动和免复制代码时,我写了以下内容:

#include <functional>
#include <type_traits>
#include <utility>

#define FWD(...) ::std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)

namespace {
    template <typename Fn>
    class wrapped_fn
    {
    public:
        template <typename F>
        explicit wrapped_fn(F&& fn)
            : fn_{FWD(fn)}
        {}

        auto foo() &&
        {
            return wrapped_fn<Fn>{
                FWD(fn_),
            };
        }

        auto trigger_segfault() &&
        {
            return FWD(fn_)();
        }

    private:
        Fn&& fn_;
    };

    template <typename F>
    auto wrap(F&& f)
    {
        return ::wrapped_fn<F>{
            FWD(f),
        };
    }

    template <typename F>
    auto frobnicate(F&& callable)
    {
        return ::wrap([&callable] {
            return callable(); //
        });
    }

    std::function<int()> call_me()
    {
        return [] { return 42; };
    }
}

int main()
{
    return ::frobnicate(call_me())
        .foo()
        .trigger_segfault();
}

我希望这段代码能够编译并正常工作(返回代码为 42 )。因为我只是维护对函数的引用,直到调用 .trigger_function()。并且绑定(bind)到引用的临时对象对于完整表达式是有效的(直到 ; ),我不应该有任何悬空的引用或拷贝/移动。

那么,为什么在使用 gcc 编译时会出现这个段错误?还是 MSVC?


通过使用 gdb,我确定调用了 wrapped_fn .foo() 中的构造函数成员函数是问题症状开始显现的地方。在构造函数调用的开始,我们有:

(gdb) p fn
$10 = ((anonymous namespace)::<lambda()> &&) @0x7ffffffecdd0: {__callable = @0x7ffffffeced0}
(gdb) p fn.__callable
$11 = (std::function<int()> &) @0x7ffffffeced0: {<std::_Maybe_unary_or_binary_function<int>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16,
    static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x0, _M_const_object = 0x0, _M_function_pointer = 0x0, _M_member_pointer = NULL},
      _M_pod_data = '\000' <repeats 15 times>},
    _M_manager = 0x4ba0d2 <std::_Function_base::_Base_manager<(anonymous namespace)::call_me()::<lambda()> >::_M_manager(std::_Any_data &, const std::_Any_data &, std::
_Manager_operation)>}, _M_invoker = 0x4ba0b0 <std::_Function_handler<int(), (anonymous namespace)::call_me()::<lambda()> >::_M_invoke(const std::_Any_data &)>}

fn_ 初始化后成员(member),我们有:

(gdb) p fn
$12 = ((anonymous namespace)::<lambda()> &&) @0x7ffffffecdd0: {__callable = @0x7ffffffecdd0}
(gdb) p fn.__callable
$13 = (std::function<int()> &) @0x7ffffffecdd0: {<std::_Maybe_unary_or_binary_function<int>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16,
    static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x7ffffffecdd0, _M_const_object = 0x7ffffffecdd0, _M_function_pointer = 0x7ffffffecdd0,
        _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x7ffffffecdd0, this adjustment -8574455321466846208},
      _M_pod_data = "<garbage data>"}, _M_manager = 0x7ffffffecf10}, _M_invoker = 0x4b9be6 <main()+83>}

__callable成员(member)变了我不明白为什么,因为两者都是fn_fn是引用,FWD(fn)应该只是保留 fn 类别的类型转换. fn绝对不会被复制或移动,因为我捕获了一个变量,该变量计算对特殊成员函数的调用并且计数不会增加。

最佳答案

frobnicate() 中的 lambda 函数一直存在,直到 frobnicate() 返回。它仅被引用,并且将事物绑定(bind)到引用通常不会使对象保持事件状态。也就是说,从 frobnicate() 返回的对象引用了一个已销毁的对象,接触它会导致未定义的行为。

引用使对象保持事件状态的情况是将临时对象立即绑定(bind)到本地引用。即使这样,也假设临时文件没有以任何形式隐藏。例如,将临时包装成任何形式的调用时,它不起作用。

关于c++ - 为什么这段代码涉及使用对临时段错误的引用,尽管它似乎正确地管理了生命周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54704217/

相关文章:

c# - C++ 中 cv::Rectangle 的问题

c++ - 使用可执行输出构建 ffmpeg

c++ - 具有类型模板参数默认值的非类型模板参数

c - 以不确定的顺序使用具有副作用的函数是否存在不确定的行为?

c++ - 如何访问新的 c++11 标准大小的类型?

c++ - 非阻塞连接超时

c++ - 具有最少复制的正向仿函数

c++ - 根据模板类型生成 lambda 主体(调用可调用对象并返回)

c - 从 double 到 float 的转换是否总是潜在地调用未定义的行为?

c - C 中意外的位移行为