2

我正在编写一个包装类来将自定义结构成员变量包装成变体。给定对结构对象的引用,std::function 必须返回一个变体,其中包含特定成员的值 - 最初创建 std::function 时使用该成员。这意味着,我需要在创建函数时保存指向指定类成员的指针。

此代码可以编译,但是当我尝试调用函数对象时,出现分段错误。为什么会发生以及如何解决?

#include <functional>
#include <variant>

#include <iostream>

struct Foo
{
    int a,
    b;
    char c;
    double d,
    e;
};

template <typename Struct, typename ... VarTypes>
struct WrapMemberGet
{
    template <typename T>
    static std::function<std::variant<VarTypes...>(const Struct&)>
            WrapGet(T(Struct::*mem_ptr))
    {
                return [&](const Struct& s)
                {
                    return std::variant<VarTypes...>(s.*mem_ptr);
                };
    }
};

int main(int argc, const char **argv)
{
    Foo  f{4, 8, 15, 16, 23};

    auto get_a = WrapMemberGet<Foo, char, int ,double>::WrapGet(&Foo::a);
    std::variant<char, int, double> var = get_a(f); // this must hold int = 4


    return 0;
}
4

2 回答 2

4

WrapGet的函数按值获取指向成员函数的指针,并在函数内部将该指针捕获为 lambda 中的引用。这意味着在函数结束后,使用 lambda 内部的引用是悬空的,并且使用它会调用 UB。

为了避免它通过值捕获它,而是在 lambda 中创建一个本地副本。

return [=](const Struct& s)
{
    return std::variant<VarTypes...>(s.*mem_ptr);
};
于 2020-05-20T11:42:42.263 回答
3

通过复制捕获以避免悬空引用:

template <typename T>
static std::function<std::variant<VarTypes...>(const Struct&)>
WrapGet(T(Struct::*mem_ptr))
{
    return [=](const Struct& s)
    //      ^
        {
            return std::variant<VarTypes...>(s.*mem_ptr);
        };
}
于 2020-05-20T11:39:11.543 回答