首页 > 解决方案 > 任何类成员函数类型

问题描述

我的最终目标是什么?

我希望我的库的用户能够指定任意字段名称的映射以及与这些字段名称关联的成员函数指针。一个例子如下:

class Foo {
private:
    int field_1_;
    double field_2_;

public:
    int field_1() const { return field_1_; }
    double field_2() const { return field_2_; }
};

std::map<std::string, any_mem_fn> fields = {
    { "field_1", &Foo::field_1 },
    { "field_2", &Foo::field_2 }
}; 

如您所见,问题在于成员函数签名可能不同。

我试过什么?

我尝试创建any_mem_fn存储任何成员函数指针的类。类如下所示:

class any_mem_fn {
    struct base_mem_fn_ptr {
        virtual ~base_mem_fn_ptr() {}
        virtual double operator()(Foo& foo) = 0;
    };

    template <typename F>
    struct mem_fn_ptr : base_mem_fn_ptr {
        F fn;

        mem_fn_ptr(F&& f)
            : fn(std::forward<F>(f))
        {}

        double operator()(Foo& foo) override {
            return std::invoke(fn, foo);
        }
    };

public:
    any_mem_fn() : fn_(nullptr) {}
    any_mem_fn(any_mem_fn&& rhs) = default;
    any_mem_fn(any_mem_fn const& rhs) = default;

    template <typename F>
    any_mem_fn(F&& f) {
        fn_ = std::make_shared<mem_fn_ptr<F>>(std::forward<F>(f));
    }

    any_mem_fn& operator=(any_mem_fn&& rhs) = default;
    any_mem_fn& operator=(any_mem_fn const& rhs) = default;

    ~any_mem_fn() = default;

    template <typename T>
    double value(T&& obj) {
        return std::invoke(*fn_, obj);
    }

private:
    std::shared_ptr<base_mem_fn_ptr> fn_;
};

在这里,如您所见,我使用多态性进行类型擦除。此外,您可能会注意到,operator()如果每个函数签名都返回double. 这是故意的,目前不是我的问题,所以请不要参考这个。

现在,上面的类有什么问题......问题是它只适用于Foo类的成员函数。如果我尝试存储Bar类的成员函数,上面的代码将不起作用。为了使上述代码在任何类的情况下都能正常工作,我需要operator()为每个我不能做的特定类重载,因为我的库不知道库用户拥有的所有类。

完整的演示在这里

我正在研究的图书馆在这里

问题:是否有可能以某种方式将任何类成员函数存储在一个类中,并能够稍后在对象上调用该成员函数?

标签: c++c++17

解决方案


不确定这是您想要的,但使用 std::function 和 std::any,您可能会执行以下操作:

class any_mem_fn {
public:
    template <typename Ret, typename C>
    any_mem_fn(Ret (C::*m)()) {
        fn_ = [m](std::any a) { return (std::any_cast<C>(a).*m)(); };
    }
    // Possibly provide overloads for combination const/volatile, reference...

    template <typename T>
    double value(T obj) { return fn_(obj); }

private:
    std::function<double(std::any)> fn_;
};

演示


推荐阅读