将来自任意 class 的成员函数指针存储为 class 实例变量

Storing member function pointer from arbitrary class as class instance variable

SO 上有几个问题将函数指针传递为 parameters/arguments(here, here, here, etc.). In fact, I asked a 前几天。但是,这个问题有点不同。

我的问题是我正在写一个class,我想非常灵活。

我现在所拥有的适用于 non-member 功能。贴在下面

template <typename T>
class MyClass
{
    private:
        typedef double (*firstFunctionPtr) (const T &var);

        typedef bool (*secondFunctionPtr)(const T &var);

        // Function pointers as member variables
        firstFunctionPtr _firstFunc;
        secondFunctionPtr _secondFunc;

    public:
        inline MyClass(firstFunctionPtr firstFunc, 
            secondFunctionPtr secondFunc);
};

template<typename T>
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
    _firstFunc(firstFunc),
    _secondFunc(secondFunc),
    {}

但是,当我需要使用指向其他任意 class 的成员函数的指针进行初始化时,这会崩溃,不幸的是,对于我来说,这恰好是我的目的的常见用例.

This answer 建议

In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types.

但是,我无法编译。我试过模板化我的 typedef(使用 C++11 别名方法),我试过向 class 添加第二个模板参数来处理这些成员函数的调用 class,但是两种方法都无效。

This Q/A 似乎正朝着我想做的方向发展,但我无法弄清它的正反面。

  1. 有人可以解释我如何修改我的 class 来处理传入的任意成员函数指针吗?
  2. 此外,是否可以使其可以处理任意成员函数或non-member函数?
  3. 最后,是否可以使用模板来实现?

郑重声明,我尽量避免使用 functional header,但不使用它可能是愚蠢的事。

您需要做的就是bind成员函数指针的对象实例作为第一个参数。

struct foo {
    float bar1(const type &var);
    bool bar2(const type &var);
};
foo my_foo;
auto f1 = std::bind(&foo::bar1, my_foo, _1);
auto f2 = std::bind(&foo::bar2, my_foo, _1);
MyClass<type> my_obj(f1, f2);

如果你想让MyClass成为一个既能兼顾自由功能的模板 类型指针:

double (*)(const T &var);
bool (*)(const T &var);

对于某些参数类型 T,或者 member-function 类型指针:

double (C::*)(const T &var);
bool (C::*)(const T &var);

对于某些参数类型 CT 那么, MyClass 必须被参数化 TC 并且你需要两个专业:

  1. 其中 C 是一些 non-class 类型
  2. 其中 C 是任何 class 类型

在情况(1)中,non-class类型C不可能有成员函数, 这样就可以实现 free-function 指针特化。

在情况 (2) 中,class C 可能是一个具有成员函数的函数,因此一个 将实现 member-function 指针特化。

non-class 类型 C 的明显选择是 void。所以我们可以C 默认为 void:

主模板

template<typename T, typename C = void>
struct MyClass;

这样:

MyClass<T>

将是 T 的自由函数指针特化,并且:

MyClass<T,C>

对于 void 以外的任何 C,都将是 member-function 指针特化。

如您所知,您可以使用 std::enable_ifSFINAE 使编译器 选择 class 模板的一种专业化或另一种专业化,具体取决于一个 它的模板参数 U 满足一些编译时测试。你可以拿 此处的方法,但另一种不需要该设备的方法可用:

从主模板开始,我们只想拥有:

自由函数专业化

template<typename T>
struct MyClass<T>
{
    ... for free function pointers ...
};

和:

成员函数特化

template<typename T, typename C>
struct MyClass<T,C>
{
    ... for member function pointers ...
};

但我们不能只这样,因为成员函数 "specialization" 恰好有 与主模板相同的模板参数。这意味着它 不是 专业化,编译器不允许。

不过,您可以轻松解决该问题,只需将主 template 一个它不需要的默认模板参数,但是它的 存在允许这两个专业化。

新建主模板

template <typename T, typename C = void, typename Default = void> 
struct MyClass;

所以这是一个说明性的解决方案:

// Primary template
template <typename T, typename C = void, typename Default = void> 
struct MyClass;

// Free function specialization
template <typename T>
struct MyClass<T>
{
    using firstFunctor_t = double(*)(T const &);
    using secondFunctor_t = bool(*)(T const &);

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
    :   _firstFunc(firstFunc),
        _secondFunc(secondFunc)
    {}

    double callFirst(T const & var) {
        return _firstFunc(var);
    }

    bool callSecond(T const & var) {
        return _secondFunc(var);
    }

private:

    firstFunctor_t _firstFunc;
    secondFunctor_t _secondFunc;
};

// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
    using firstFunctor_t = double(C::*)(T const &);
    using secondFunctor_t = bool(C::*)(T const &) const;

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
    :   _firstFunc(firstFunc),
        _secondFunc(secondFunc)
    {}

    double callFirst(C & obj, T const & var) {
        return (obj.*_firstFunc)(var);
    }

    double callFirst(C const & obj, T const & var) {
        auto & o = const_cast<C&>(obj);
        return (o.*_firstFunc)(var);
    }

    bool callSecond(C & obj, T const & var) {
        return (obj.*_secondFunc)(var);
    }

    bool callSecond(C const & obj, T const & var) {
        auto & o = const_cast<C&>(obj);
        return (o.*_secondFunc)(var);
    }

private:

    firstFunctor_t _firstFunc;
    secondFunctor_t _secondFunc;
};

在成员函数专业化中,请注意您可能会遇到的几点 没有考虑过:-

我决定要存储的第二个成员函数应该是 const 成员函数。 C 的成员函数很有可能 采用 T const & 参数并且 returns bool 将是 const 成员 功能,不是吗?如果是这样,那么 const-ness 必须是 我在专业化中使用的 member-function 类型定义:

using secondFunctor_t = bool(C::*)(T const &) const;

或尝试用任何 bool (C::*)(T const &) const 实例化特化 将无法编译。

另外,我为每个 MyClass<T,C>::callFirst 提供了两个重载 和 MyClass<T,C>::callSecond,一个带有参数:

C & obj, T const & var

和另一个有参数的:

C const & obj, T const & var

没有第二个,尝试调用 MyClass<T,C>::callFirstMyClass<T,C>::callSecondobj 是 const 将失败 编译。

对于演示此解决方案的程序,您可以附加:

#include <iostream>
#include <string>

double foo(std::string const & s)
{
    return std::stod(s);
}

bool bar(std::string const & s)
{
    return s.size() > 0;
}

struct SomeClass
{
    SomeClass(){};
    double foo(std::string const & s) {
        return ::foo(s);
    }

    bool bar(std::string const & s) const {
        return ::bar(s);
    }
};

int main()
{
    MyClass<std::string> my0{foo,bar};
    std::cout << std::boolalpha;
    std::cout << my0.callFirst("1.11") << std::endl;
    std::cout << my0.callSecond("Hello World") << std::endl;

    MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
    SomeClass thing;
    std::cout << my1.callFirst(thing,"2.22") << std::endl;
    std::cout << my1.callSecond(thing,"Hello World") << std::endl;

    SomeClass const constThing;
    std::cout << my1.callFirst(constThing,"3.33") << std::endl;
    std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
    return 0;
}

See it live

您说您希望此模板为 "extremely flexible"。这 图示解决方案适合您的示例,但您可能 有兴趣知道它 nearly 没有您所能得到的那么灵活。 对于自由函数和成员函数,附加 variadic template 参数,您的模板可以存储和调用 [member] 函数 任意 return 类型和任意数量的任意类型参数。 参见 和 回答。

我建议创建一个帮助对象来存储您要使用的类型:

template <typename RETURN, typename TYPE, typename CLASS>
struct function_pointer
{ using type_t = RETURN (CLASS::*)(const TYPE &); };

template <typename RETURN, typename TYPE>
struct function_pointer<RETURN, TYPE, std::nullptr_t>
{ using type_t = RETURN (*)(const TYPE &); };

如果将 class 作为第三个参数提供,则此类型将创建 member-function-pointer,否则将创建 function-pointer。现在,我们可以在 MyClass:

中使用这个助手
template <typename T, typename CLASS = std::nullptr_t>
class MyClass
{
    using firstFunctionPtr  = typename function_pointer<double, T, CLASS>::type_t;
    using secondFunctionPtr = typename function_pointer<bool, T, CLASS>::type_t;

    // Function pointers as member variables
    firstFunctionPtr _firstFunc;
    secondFunctionPtr _secondFunc;

public:
    inline MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
        _firstFunc(firstFunc),
        _secondFunc(secondFunc)
    {}

    void call_first(CLASS &c, const T&v) { (c.*_firstFunc)(v); }
    void call_second(CLASS &c, const T&v) { (c.*_secondFunc)(v); }

    void call_first(const T&v) { (_firstFunc)(v); }
    void call_second(const T&v) { (_secondFunc)(v); }
};

我添加了 call_* 函数只是为了展示一个用例,如下所示:

// Some class with the expected function signatures
struct S1
{
    int i = 0;
    double d(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
    bool b(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n';     return{}; }
};

// Another class with the expected function signatures
struct S2
{
    double d(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
    bool b(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
};

// Free function with which could have the expected function signature
template <typename R>
R f(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }

使用 MyClass 和任意 class (S1):

S1 a{1}, b{2};
S2 c, d;
MyClass<int, S1> MCiS1(&S1::d, &S1::b);
MCiS1.call_first(a, 111);  // Prints -> 1 double S1::d(const int&)
MCiS1.call_second(b, 222); // Prints -> 2 bool S1::b(const int&)
MCiS1.call_first(c, 111);  // Error decltype(c) is not S1.
MCiS1.call_second(d, 222); // Error decltype(d) is not S1.

使用 MyClass 和不同的 class (S2):

MyClass<int, S2> MCiS2(&S2::d, &S2::b);
MCiS2.call_first(c, 111);  // Prints -> double S2::d(const int&)
MCiS2.call_second(d, 222); // Prints -> bool S2::b(const int&)
MCiS2.call_first(a, 111);  // Error decltype(c) is not S2.
MCiS2.call_second(b, 222); // Error decltype(d) is not S2.

MyClass与non-member函数结合使用:

MyClass<int> MCi(f<double>, f<bool>);
MCi.call_first(111);  // Prints -> R f(const int&) [with R = double]
MCi.call_second(222); // Prints -> R f(const int&) [with R = bool]

查看现场演示 Here.