使用绑定功能调用成员函数

时间:2012-10-27 19:31:20

标签: c++

如何实现一个非常简单的boost :: bind版本,它不绑定参数,但提供了一种在c ++类中调用成员函数的方法。

这是我的第一次尝试:

#include <iostream>

struct Foo {
    void x(int i) { std::cout << "Foo " << i << std::endl; }
};

struct Bar {
    void y(int i) { std::cout << "Bar " << i << std::endl; }
};

template<typename A1, typename I, typename M>
struct Binder {
    Binder(I i, M m) : i_(i), m_(m) { }
    void operator()(A1 a1) {
        (i_->*m_)(a1);
    }

    I i_;
    M m_;
};

template<typename A1, typename I, typename M>
Binder<A1, I, M> my_bind(I i, M m) {
    return Binder<A1, I, M>(i, m);
}

int main(int argc, const char *argv[])
{
    Foo foo;
    Bar bar;

    Binder<int, Foo*, void (Foo::*)(int)> b1 = my_bind<int>(&foo, &Foo::x);
    Binder<int, Bar*, void (Bar::*)(int)> b2 = my_bind<int>(&bar, &Bar::y);

    b1(1);
    b2(2);

    return 0;
}

上面的实现确实有效,并将打印:

Foo 1
Bar 2

问题是my_bind的两次调用返回不同类型的对象。如何更改程序,以便my_bind将返回仅依赖于A1的类型。

1 个答案:

答案 0 :(得分:0)

  

问题是my_bind的两次调用返回不同类型的对象。如何更改程序,以便my_bind将返回仅依赖于A1的类型。

可以使用type erasure

简而言之:

  1. 使用您想要的界面创建抽象类。它可能有一些模板参数。 (以下代码中的AbstractBinder)
  2. 创建实现此接口的具体类。具体类可能具有比接口更多的模板参数。 (下面的Binder类)
  3. 使用模板构造函数创建 Holder 类 - 它创建 Concrete 类,但仅存储指向其基本 Abstract 类的指针。因此, Holder 类只包含Abstract接口所需的模板参数,而其构造函数具有 Concrete 类所需的所有其余模板参数。 (下面是BinderHolder课程)
  4. live demo

    用法:

    int main()
    {
        Foo foo;
        Bar bar;
    
        BinderHolder<int> b1 = my_bind<int>(&foo, &Foo::x);
        BinderHolder<int> b2 = my_bind<int>(&bar, &Bar::y);
    
        b1(1);
        b2(2);
    }
    

    完整代码:

    template<typename A1>
    struct AbstractBinder
    {
        virtual void call(A1 a1)=0;
        virtual AbstractBinder<A1> *clone()=0;
        virtual ~AbstractBinder(){}
    };
    
    template<typename A1, typename I, typename M>
    struct Binder : AbstractBinder<A1>
    {
        Binder(I i, M m) : i_(i), m_(m) { }
        void call(A1 a1)
        {
            (i_->*m_)(a1);
        }
        virtual AbstractBinder<A1> *clone()
        {
            return new Binder(*this);
        }
        I i_;
        M m_;
    };
    
    template<typename A1>
    class BinderHolder
    {
        AbstractBinder<A1> *ptr;
        BinderHolder &operator=(const BinderHolder&);
    public:
        template<typename I, typename M>
        BinderHolder(I i, M m)
            : ptr(new Binder<A1,I,M>(i,m))
        {
        }
        BinderHolder(const BinderHolder &rhs)
            : ptr(rhs.ptr->clone())
        {
        }
        ~BinderHolder()
        {
            delete ptr;
        }
        void operator()(A1 a1)
        {
            ptr->call(a1);
        }
    };
    
    template<typename A1, typename I, typename M>
    BinderHolder<A1> my_bind(I i, M m) {
        return BinderHolder<A1>(i, m);
    }
    
    #include <iostream>
    
    struct Foo {
        void x(int i) { std::cout << "Foo " << i << std::endl; }
    };
    
    struct Bar {
        void y(int i) { std::cout << "Bar " << i << std::endl; }
    };
    
    int main()
    {
        Foo foo;
        Bar bar;
    
        BinderHolder<int> b1 = my_bind<int>(&foo, &Foo::x);
        BinderHolder<int> b2 = my_bind<int>(&bar, &Bar::y);
    
        b1(1);
        b2(2);
    }
    

    P.S。如果您确定所有具体类都具有相同的大小,那么您可以将堆分配替换为new inside inside到fixed size buffer,并添加static_assert以确保安全。