【淺析C++11】std::function和std::bind


std::function可調用對象包裝器

C++可調用對象(Callable Objects)定義如下:

  1. 函數指針:與C語言一致;
  2. 類成員函數指針;
  3. 仿函數(functor):也成函數對象,重載operator()運算符的類/結構體對象;
  4. lambda表達式。

std::function是Callable Objects的包裝器(Wrapper),可接收除了類成員函數指針以外的任意Callable Objects。std::function可用來處理函數回調,與C語言函數指針類似,允許保存以上Callable Objects,並延遲執行它們,但它可保存除函數指針外的其他Callable Objects,因此它比C語言更強大。

當我們為std::function具現化一個函數簽名(函數類型,包括返回值和參數列表),它就成為一個可容納所有這類調用的函數包裝器。

std::function基本用法

#include <iostream>
#include <functional> // std::function

// global function
void func(void) {
  std::cout << __FUNCTION__ << std::endl;
}

class Foo {
public:
  // class static function
  static int foo_func(int a) {
    std::cout << __FUNCTION__ << "(" << a << "):";
    return a;
  }

  // class non-static member function
  int foo_func_nonstatic(int a) {
    std::cout << __FUNCTION__ << "(" << a  << "):";
    return a;
  }
};

class Bar {
public:
  // functor
  int operator()(int a) {
    std::cout << __FUNCTION__ << "(" << a << "):";
    return a;
  }
};

int main() {
  // 傳入合適函數簽名給std::function模板參數即可綁定對應簽名的
  // 普通函數或
  // 類靜態成員函數或
  // 借助std::bind綁定類非靜態成員函數
  std::function<void(void)> func1 = func;
  std::function<int(int)> func2   = Foo::foo_func;
  Foo foo;
  std::function<int(int)> func3   = std::bind(&Foo::foo_func_nonstatic, &foo,
                                              std::placeholders::_1);

  // 然后,直接像函數一樣調用
  func1(); // func
  std::cout << func2(1)  << std::endl; // foo_func(1):1
  std::cout << func3(11) << std::endl; // foo_func_nonstatic(11):11

  // 當函數簽名一致時,func2可像一個變量一樣復用
  // Bar重載了operator()即成為functor,可直接包裝到std::function
  Bar bar;
  func2 = bar;
  std::cout << func2(2) << std::endl; // operator()(2):2

  // 也可綁定lambda表達式
  auto func_lambda = [](int a){
    std::cout << "bind lambda sample(" << a << ")" << std::endl;
  };
  func_lambda(3); // bind lambda sample(3)

  return 0;
}

既然std::function可作為左值接收函數對象,那么它可作為函數的形參;且std::function可綁定函數指針,實現函數的延遲執行,所以std::function可取代std::function作為回調函數。

std::function實現回調機制的例子如下:

#include <iostream>
#include <functional> // std::function

// 任意可調用對象,如普通全局函數
void func_callback(int a) {
  std::cout << __FUNCTION__ << ":Output, a=" << a << std::endl;
}

class Foo {
public:
  explicit Foo(std::function<void(int)> cb)
    :cb_(cb), a_(0) {
  }
  ~Foo() = default;

  // setter
  void set_a(int a) {
    a_ = a;
  }

  void OutputCallback() {
    cb_(a_);
  }

private:
  std::function<void(int)> cb_;
  int a_;
};

int main() {
  // 實例化Foo,並參數注入回調函數
  // 處理
  // 回調輸出處理結果
  Foo foo(func_callback);
  foo.set_a(1);
  foo.OutputCallback(); // func_callback:Output, a=1

  return 0;
}

以上,介紹了std::function綁定Callable Objects的用法,並以一個實例演示了std::function作為函數回調的例子;其中,func_callback回調函數參數為int型,在實際應用如人臉智能分析中,人臉分析信息結構體指針可作為回調函數參數,該函數可輸出人臉識別結果;Foo類可為人臉分析相關類,OutputCallback可能在該類某個人臉分析線程中被調用,分析得到的結果可調用OutputCallback回調輸出給提供func_callback的用戶。

注:C++11提供的std::function可替代C語言中函數指針作為回調函數,前者是C++編程風格,后者是C編程風格。

C/C++回調機制在服務器端並發編程和游戲領域應用廣泛,著名2d游戲框架Cocos2dx大量使用回調機制,提供了常用的回調宏如下所示,更多回調機制可參考Cocos2dx開源源碼。

// Cocos2dx new callbacks based on C++11
//
// __selector__:回調函數指針
// __target__:回調對象指針
// ##__VA_ARGS__:可變參數列表 
// std::placeholders::_1:不定參數1,調用時由調用函數的參數傳入
// std::placeholders::_2:不定參數2,調用時由調用函數的參數傳入
// std::placeholders::_3:不定參數3,調用時由調用函數的參數傳入
#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__)

std::function/std::bind與抽象工廠、工廠方法的一點思考

TODO...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM