C++11 std:functionとstd:bind使用詳細


cococococos newは新しいプロジェクトを出した後、コードをよく読んで、3.0と2.0を区別するコードを発見しました。

auto closeItem = MenuItemImage::create(
                      "CloseNormal.png",
                      "CloseSelected.png",
                          CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
2.0内のコードはCC_ではありません。CALLBACK_1はmenu_ですselector.
CCCALLBACKシリーズは、3.0 c+11の特性に基づいて追加されました。CCCALLBACKシリーズの定義は以下の通りです。

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
わかるように、CC_CALL_バックシステムの後の数字は、関数ポインタのパラメータの個数を表します。この点が分かりました。CC_を選択します。CALLBACKの場合、間違えないです。
サンプルコードを見ると、面白い使い方があります。

listener->onTouchesBegan = CC_CALLBACK_2(Layer::onTouchesBegan, this);
この時に思わずお聞きしたいのですが、なぜ直接関数ポインタに値が付けられないですか?
定義を見れば分かります。

std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;
CCなのでCALLBACKシリーズはstd:bind、ontouches Beganはstd:functionで定義されています。じゃstd::bindとstd::functionは何の違いがありますか?
ブログによると、
functionテンプレートクラスとbindテンプレート関数は、類似の関数ポインタの機能を実現することができますが、関数ポインタよりも柔軟で、特に関数指向クラスの非静的メンバー関数の場合に使用されます。
std:functionは、大域関数/クラス静的メンバ関数(クラス静的メンバ関数と大域関数は区別されていません)に結びつけることができます。クラスの非静的メンバ関数に結びつけるには、std:bindが必要です。
標準ライブラリ関数bind()とfunction()は、関数および関数パラメータを処理するために、ヘッダファイルに定義されています。
std::bindバインド
  • は、関数、メンバ関数、およびクローズドをfunction関数オブジェクト
  • に変換する。
  • は、多元(n>1)関数を一要素関数または(n−1)要素関数に変換する。
  • bind()は、関数(または関数オブジェクト、または任意の記号で呼び出すことができるもの)を受け取り、ある関数パラメータが「バインディング」または再組織された関数オブジェクトを生成します。名前の通り、bind()関数の意味は、その関数名のように、関数呼び出しのいくつかのパラメータを結びつけるために使われます。たとえば:
    
    int f(int, char, double);
    auto ff = bind(f, _1, 'c', 1.2);   //   f()              ,           ff,      int     
    int x = ff(7);            // f(7, 'c', 1.2);
    
    パラメータの結合は、一般に「Currying」と呼ばれ、「カレーの煮付け」は、関数または関数の対象を加工して修飾することを意味します。1は、関数fが関数ffを介して呼び出されたときの関数ffの最初のパラメータの関数fのパラメータリストの位置を表すプレースホルダオブジェクトです。最初のパラメータを'_'と呼びます。1「2番目のパラメータは」_2「これによって類推する。たとえば:
    
    int f(int, char, double);
    auto frev = bind(f, _3, _2, _1);    //       
    int x = frev(1.2, 'c', 7);       // f(7, 'c', 1.2);
    
    ここで、autキーワードは、bindが戻ってくる結果のタイプを推測する作業を節約しています。
    私たちはビッド()を使用して一つの重負荷関数のパラメータを結びつけることができません。結合が必要な重負荷関数のバージョンを明示的に指摘しなければなりません。
    
    int g(int);
    double g(double);
    
    auto g1 = bind(g, _1);             //   :     g() ?
    auto g2 = bind( (double(*)(double))g, _1);  //   ,      
    
    
    
    void H(int a);
    //      
    auto f11 = std::bind(H, std::placeholders::_1);
    auto       std::function<void(int)>
    
    //          
    std::function<void (char*, int)> f = std::bind(&ReadHandler::ConnectPreProcess, this, std::placeholders::_1, std::placeholders::_1);
    
    //           
    int f(int, char, double);
    //   f()              ,
    //            ff,      int     
    auto ff = bind(f, _1, ‘c', 1.2);  
    int x = ff(7);
    
    
    自分でコードを書く例は以下の通りです。
    
    int Func(int x, int y);
    auto bf1 = std::bind(Func, 10, std::placeholders::_1);
    bf1(20); ///< same as Func(10, 20)
    
    int HelloWorld::AddFunc( int a, int b )
    {
      return a + b;
    }
    
    bool HelloWorld::init()
    {
    
      auto bf2 = std::bind(&HelloWorld::AddFunc,this , std::placeholders::_1, std::placeholders::_2 );
      auto result1 = bf2(10, 20); ///< same as a.Func(10, 20)
    
      std::function< int(int)> bf3 = std::bind(&HelloWorld::AddFunc, this, std::placeholders::_1, 100);
      auto result2 = bf3(10); ///< same as a.Func(10, 100)
    
    }
    
    
    上記の例では、bf 1は、1つの2つのパラメータ普通関数の最初のパラメータを10に結合し、新しい1つのパラメータを生成した呼び出し可能なエンティティである。bf 2は、クラスのメンバー関数をクラスのオブジェクトにバインドし、通常の関数のような新しい呼び出し可能なエンティティを生成する。bf 3はクラスメンバー関数をクラスオブジェクトと第二のパラメータに結びつけて、新しいstd:functionオブジェクトを生成した。上の例が分かりました。ビッドを使うには注意しなければならないことを説明します。
    (1)bindあらかじめ結合されたパラメータは、特定の変数または値を送る必要があり、あらかじめ結合されたパラメータに対しては、pass-by-valueのものである。
    (2)事前に縛られていないパラメータについては、stdを伝える必要があります。1から順に増加します。placceholderはpass-by-referenceのです。
    (3)bindの戻り値は、エンティティを呼び出すことができ、std:functionオブジェクトを直接付与することができます。
    (4)結合されたポインタ、参照タイプのパラメータについて、使用者は、エンティティ呼び出しを呼び出す前に、これらのパラメータが利用可能であることを保証する必要がある。
    (5)クラスのthisは、オブジェクトまたはポインタで結合できます。
    std::function
    関数、関数オブジェクト、関数ポインタ、およびメンバー関数の包装器です。関数オブジェクト、関数ポインタ、参照関数、メンバー関数のポインタは、任意のタイプの関数オブジェクトを格納することができます。
    関数、関数オブジェクト、関数ポインタ、およびメンバー関数を統一的に処理します。関数の保存と遅延を許可します。
    関数とメンバー関数をfunctionとします。
    functionは、「(...)」記号で呼び出すことができる任意の値を持つタイプです。特に、bindの戻り結果は、functionタイプに値を割り当てることができる。functionはとても使いやすいです。より直感的に、関数を表すデータの種類として、関数オブジェクトのように機能します。ただ普通のデータタイプはデータを表しています。functionは関数という抽象概念を表しています。)たとえば:
    
    typedef std::function<float (int x, int y)> f ;//         ,            float,     int,int    
    struct int_div {    //         "()"            
      float operator() (int x, int y) const { return ((float)x)/y; };
    };
    
    void HelloWorld::testing()
    {
      f f1= int_div();          //    
      auto result3 = f1( 10, 2);
    }
    
    
    メンバ関数は、追加のパラメータを伴う自由関数として見なされ得る。
    
    struct int_div {    //         "()"            
      float operator() (int x, int y) const { return ((float)x)/y; };
      int int_div_fun( int x ){ return x; };
    };
    typedef std::function<int (int_div*, int)> f_2;
    
    bool HelloWorld::init()
    {
      f_2 f2 = std::mem_fn(&int_div::int_div_fun);      //       
    
      int_div int_div_object;
      int v = f2(&int_div_object, 5); //    x    5  X::foo()
      std::function<int (int)> ff = std::bind( f2, &int_div_object, std::placeholders::_1);  // f       &x
      v = ff(5);        //   x.foo(5)
    
    
    }
    
    
    ps:vs 2012のbugに穴を開けられました。ネットのコードを見たので、最初の9行はこう書きました。2 f 2=&int_div::int_div_ふうふ
    そしてエラーを報告します。Err 1 error C 2664:'std::uFun_クラスRet,_V 0_t,_V 1_t>:_Set':cannot convert parameter 1 from'uMyimpl*'to'std:_Fun_ベースRx,_V 0_t,_V 1_t>*'
    調べてみましたが、vs 2010にはこのコンパイルの誤りはありませんでしたが、2012にあります。2012はstdを加えなければならない:mem_fnでコンパイルできます。
    関数ポインタをfunctionで置換できます。関数を保存して実行を遅らせることができますので、コールバック関数としても適しています。また、cxiの中の特別な依頼に似ています。メンバー一人だけの依頼です。
    
    struct int_div { //         "()"            
      float operator() (int x, int y) const { return ((float)x)/y; };
      int int_div_fun( int x ){ return x; };
    
      int_div( std::function<void()>& f ):m_callback(f){};
      void Notify()
      {
        m_callback();
      }
      std::function<void()> m_callback;
    };
    
    
    Functionは関数としても参加できます。このように関数の外部制御関数の内部挙動により、私たちの関数がより柔軟になります。
    
    void Foo(int x, std::function<void(int)>& f)
    {
      if(x%2==0)
      f(x);
    }
    
    void G(int x)
    {
      cout<<x<<endl;
    }
    
    void H(int x)
    {
      cout<<x+2<<endl;
    }
    
    void TestFoo()
    {
      auto f = std::bind(G, std::placeholders::_1); 
      Foo(4, f);
    
      // Foo      f   
      f = std::bind(H, std::placeholders::_1);
      Foo(4, f);
    }
    
    
    c+11は関数のオブジェクトを一般化するために、関数の指針、参照関数、メンバー関数の指針を発表しました。ビッドを出すのは前の標準倉庫のbind 1 stとbind 2 stを交替して強化するためで、私達のを使用して更に便利になります。
     ここでC++11 stdについて:functionとstd::bindは詳細な文章を使ってここに紹介します。もっと関連しているC++11 std::functionとstd::bindの内容は以前の文章または以下の関連記事を検索してください。これからもよろしくお願いします。