0

C++0X では、可変個引数テンプレートを使用して汎用の呼び出し元/コールバック関数を記述したいと考えています。最初のハードル: 呼び出し先はメンバー関数です。ここまでは順調ですね。2 番目のハードル: 同じ名前のオーバーロードされたメンバー関数が多数あります。

どうすればこれを解決できますか? 私の主な参考文献はこのすばらしい記事ですが、うまく機能させることはできません。

それでは、詳しく見ていきましょう:

Class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15(&Foo::bar, this, 17, 29); // want first version
    }
};

ただし、オーバーロードによってテンプレートの解決が妨げられるため、yum() で呼び出しをコンパイルできません。参照記事によると、関数オブジェクトを明示的に作成する必要があります

f = magic::make_function<help, me>(&Foo::bar)

そしてのんびり電話sendBarToID_15(f, this, 17, 29)

  1. どうすればこれを機能させることができますか?

  2. 最後の行の「this」を不要にする std::bind マジックのボーナス ポイント。

  3. 便利な方法で 15 のパラメトリックを作成するための追加のボーナス ポイント。

どうもありがとう!

4

4 に答える 4

2

これはあなたが探しているものですか?

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, int, int) { std::cout << "here\n"; }
    void bar(int ID) { /*...*/ }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<int ID, typename ... Args>
    void sendBarToID(std::function<void(int, Args...)> refB, Args&& ... args)
    {
        refB(ID, std::forward<Args>(args)...);
    }

public:
    void yum()
    {
        using namespace std::placeholders;
        void (Foo::*mfp)(int, int, int) = &Foo::bar;
        sendBarToID<15>(std::function<void(int, int, int)>
            (std::bind(mfp, this, _1, _2, _3)), 17, 29); // want first version
    }
};

int main()
{
    Foo foo;
    foo.yum();
}
于 2011-04-10T00:21:30.400 に答える
1

ラムダ関数を使用してください。この種のことはもう必要ありません。

class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15([&, this](int i) {
            this->bar(i, 17, 29);
        });
    }
};
于 2011-04-10T13:27:15.697 に答える
0

次の引数を順番に使用して bar を呼び出そうとしています: 15、this、17、29。

欲しいもの:これ、15、17、29

template<typename ... Args>
void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
{
    refB(15, args...);
}

したがって、 &Foo::bar は std::function にはなりません

ラムダを使用できる場合は、次を使用します。

 void yum()
 {
    // EDITED: was "Foo* t"
    // (I don't remember of capture works with this, you may not need this)
    Foo* p = this;
    sendBarToID_15([p](int x, int y, int z){ p->bar(x, y, z); }, 17, 29);
 }

ヘルパー クラスで実装できない場合:

class helper {
private:
   Foo* p;
public:
   [...]
   void operator(int x, int y, int z) {
      p->bar(x,y,z);
   }
}

またはバインドを使用:

// EDITED: wrong return type was used
void (Fred::*f)(char x, float y) = &Foo::bar;
sendBarToID_15(std::bind(f, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 17, 29);
于 2011-04-10T00:15:40.030 に答える
0

ハワードのすばらしい答えをフォローアップするために、最後に、 sendBarToID 関数をテンプレート化しても、セットアップのロジックが期待どおりに改善されないという結論に達したことを述べさせてください。とにかく bind() しなければならないので、最初にプレースホルダーをバインドしてからアンバインドする理由はありません。テンプレートなしのバージョンは次のとおりです。

void sendBarToID_15(std::function<void(int)> f)
{
    f(15);
}

void yum()
{
    // No avoiding this in the presence of overloads
    void (Foo::*mfp)(int, int, int) = &Foo::bar;

    sendBarToID_15(std::bind(mfp, this, std::placeholder::_1, 17, 29));
}

可変個引数テンプレート ソリューションによって、クライアント コードがよりシンプルになることを期待していましたが、これよりもシンプルになる方法がわかりません。可変長の #define マクロが残りを処理します。

貢献していただきありがとうございます!

更新:わかりました。プリプロセッサ マクロのおかげで、最終的に思いついたのは次のとおりです。

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, const int &, int)
    { std::cout << "v1 called with ID " << ID << "\n"; }
    void bar(int ID)
    { std::cout << "v2 called with ID " << ID << "\n"; }
    void bar(int ID, double, float, void(Baz::*)()) const
    { std::cout << "v3 called with ID " << ID << "\n"; }

    void innocent(int ID, double)
    { std::cout << "innocent called with ID " << ID << "\n"; }

    void very_innocent(int ID, double) const
    { std::cout << "very innocent called with ID " << ID << "\n"; }

    template<int ID> void sendBarToID(std::function<void(int)> refB) { refB(ID); }
    template<int ID> void sendConstBarToID(std::function<void(int)> refB) const { refB(ID); }

#define MAKE_CALLBACK(f, ...) std::bind(&Foo::f, this, std::placeholders::_1, __VA_ARGS__)
#define MAKE_EXPLICIT_CALLBACK(g, ...) std::bind(g, this, std::placeholders::_1, __VA_ARGS__)

#define MAKE_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE>(&Foo::h), __VA_ARGS__)
#define MAKE_CONST_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE const>(&Foo::h), __VA_ARGS__)

public:
    void gobble()
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendBarToID<5>(MAKE_CALLBACK(innocent, q));
      sendConstBarToID<7>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<11>(MAKE_SIGNED_CALLBACK(bar, (int))); // can't do, too much commas
      sendBarToID<13>(MAKE_SIGNED_CALLBACK(bar, (int, const int &, int), n, 1729));
      sendConstBarToID<17>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }

    void yum() const
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendConstBarToID<2>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<-1>(MAKE_CALLBACK(innocent, q)); // Illegal in const function

      sendConstBarToID<3>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }
 };

int main()
{
    Foo foo;
    foo.yum();
    foo.gobble();
}

不便な点が 1 つあります。定数メンバー関数と非定数メンバー関数に対して、2 つの別個の関数とマクロを定義する必要があります。また、空の引数リスト (Foo::bar(int)) を処理できません。

于 2011-04-10T11:15:20.893 に答える