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

时间:2017-02-12 00:42:27

标签: c++ c++11 templates pointers member-function-pointers

关于SO的一些问题是将函数指针作为参数/参数(hereherehere等)进行处理。事实上,前几天我问related question。但是,这个问题有点不同。

我的问题是我正在写一篇我非常灵活的课程。

我现在拥有的非会员功能。它发布在

下面
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),
    {}

然而,当我需要使用指向某个其他任意类的成员函数的指针进行初始化时,这种情况就会崩溃,不幸的是,这对于我来说恰好是一个常见的用例。

This answer建议

  

在适当的C ++接口中,您可能希望查看函数对函数对象使用模板化参数来使用任意类类型。

但是,我无法进行此编译。我试过模板化我的typedef(使用C ++ 11别名方法),我尝试在类中添加第二个模板参数来处理这些成员函数的调用类,但这两种方法都没有用。

This Q/A似乎正朝着我正在努力的方向前进,但我无法做出正面或反面。

  1. 有人可以解释我如何修改我的类来处理传入的任意成员函数指针吗?
  2. 此外,是否可以使其能够处理任意成员函数或非成员函数?
  3. 最后,是否可以使用模板执行此操作?
  4. 为了记录,我试图避免使用functional标题,但是不使用它可能是个傻瓜。

3 个答案:

答案 0 :(得分:2)

如果您希望MyClass成为可以同时保存两个免费功能的模板 类型指针:

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

用于某些参数类型T,或者成员函数 类型指针:

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

对于某些参数类型CT,必须参数化MyClass TC同时需要两个专业化:

  1. 其中C是某类非类型
  2. 其中C是任何班级类型
  3. 在情况(1)中,非类型C不可能具有成员函数, 这样就可以实现自由函数指针的专门化。

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

    非类型C的明显选择是void。所以我们可以C 默认为void

    主要模板

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

    那样:

    MyClass<T>
    

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

    MyClass<T,C>
    
    对于除C以外的任何void

    将是成员函数指针特化。

    您可能知道可以使用std::enable_ifSFINAE来制作编译器 选择一个或多个类模板的一个专门化,取决于是否一个 其模板参数U满足一些编译时测试。你可以拿走 这种方法,但另一个可用,不需要该设备:

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

    自由职能专业化

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

    会员功能专业化

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

    但我们不能做到这一点,因为会员功能&#34;专业化&#34;确切地说 与主模板相同的模板参数。这意味着不是 专业化,编译器不允许它。

    但是,您可以轻松地删除该问题,只需提供主要问题即可 模板一个它不需要的默认模板参数,但是它的 存在允许这两个专业化站立。

    新的主要模板

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

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

    我决定要存储的第二个成员函数是a const 成员函数。它很可能是C的成员函数 采用T const &参数并返回bool将是const成员 功能,不是吗?如果是这样,则const-ness必须成为其中的一部分 我在专业化中使用的成员函数类型定义:

    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>::callFirst 或者MyClass<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

    你说你希望这个模板非常灵活&#34;。该 说明的解决方案适用于您的示例,但您可能是 有兴趣知道它并不像你能得到的那样灵活。 对于免费功能和成员功能,附加variadic template 参数,您的模板可以存储和调用[成员]函数 任意类型的仲裁返回类型和任意类型的仲裁数。 见this question和 答案。

答案 1 :(得分:1)

我将sugest创建一个帮助对象,它将存储您要使用的类型:

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

如果提供类作为第三个参数,则此类型将创建成员函数指针,否则创建函数指针。现在,我们可以在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与任意类(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与其他类(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与非成员函数一起使用:

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

答案 2 :(得分:0)

您需要做的就是将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);