1

引数をバインドしないが、C++ クラスでメンバー関数を呼び出す方法を提供する、boost::bind の非常に単純なエディションを実装する方法。

これが私の最初の試みです:

#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 の 2 つの呼び出しが異なるタイプのオブジェクトを返すことです。my_bind が A1 のみに依存する型を返すように、プログラムを変更するにはどうすればよいですか。

4

1 に答える 1

0

問題は、my_bind の 2 つの呼び出しが異なるタイプのオブジェクトを返すことです。my_bind が A1 のみに依存する型を返すように、プログラムを変更するにはどうすればよいですか。

型消去で可能です。

要するに:

  1. 必要なインターフェイスで抽象クラスを作成します。いくつかのテンプレート パラメータがある場合があります。(以下のコードのAbstractBinder)
  2. このインターフェースを実装するConcreteクラスを作成します。具体的なクラスには、インターフェイスよりも多くのテンプレート引数がある場合があります。(以下バインダークラス)
  3. テンプレートコンストラクターを使用してホルダークラスを作成します。これにより、 Concreteクラスが作成されますが、その基本抽象クラスへのポインターのみが格納されます。そのため、Holderクラスには Abstract インターフェイスに必要なテンプレート パラメータのみがあり、そのコンストラクタにはConcreteクラスに必要な残りのすべてのテンプレート パラメータがあります。(以下の BinderHolder クラス)

ライブデモ

使用法:

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

PSすべてのConcreteクラスが同じサイズになることが確実な場合は、ヒープ割り当てを固定サイズバッファー内の新しい配置に置き換え、安全のために static_assert を追加できます。

于 2012-10-27T20:14:46.570 に答える