C++11 中的std::function和std::bind

轉自:https://www.jianshu.com/p/f191e88dcc80

1. 可調用對象

可調用對象有一下幾種定義:

  • 是一個函數指針,參考 C++ 函數指針和函數類型
  • 是一個具有operator()成員函數的類的對象;
  • 可被轉換成函數指針的類對象;
  • 一個類成員函數指針;

C++中可調用對象的雖然都有一個比較統一的操作形式,但是定義方法五花八門,這樣就導致使用統一的方式保存可調用對象或者傳遞可調用對象時,會十分繁瑣。C++11中提供了std::function和std::bind統一了可調用對象的各種操作。

不同類型可能具有相同的調用形式,如:

 

// 普通函數
int add(int a, int b){return a+b;} 

// lambda表達式
auto mod = [](int a, int b){ return a % b;}

// 函數對象類
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};

上述三種可調用對象雖然類型不同,但是共享了一種調用形式:

 

int(int ,int)

std::function就可以將上述類型保存起來,如下:

 

std::function<int(int ,int)>  a = add; 
std::function<int(int ,int)>  b = mod ; 
std::function<int(int ,int)>  c = divide(); 

2. std::function

  • std::function 是一個可調用對象包裝器,是一個類模板,可以容納除了類成員函數指針之外的所有可調用對象,它可以用統一的方式處理函數、函數對象、函數指針,並允許保存和延遲它們的執行。
  • 定義格式:std::function<函數類型>。
  • std::function可以取代函數指針的作用,因爲它可以延遲函數的執行,特別適合作爲回調函數使用。它比普通函數指針更加的靈活和便利。

3. std::bind

可將std::bind函數看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表。

std::bind將可調用對象與其參數一起進行綁定,綁定後的結果可以使用std::function保存。std::bind主要有以下兩個作用:

  • 將可調用對象和其參數綁定成一個防函數;
  • 只綁定部分參數,減少可調用對象傳入的參數。

3.1 std::bind綁定普通函數

 

double my_divide (double x, double y) {return x/y;}
auto fn_half = std::bind (my_divide,_1,2);  
std::cout << fn_half(10) << '\n';                        // 5
  • bind的第一個參數是函數名,普通函數做實參時,會隱式轉換成函數指針。因此std::bind (my_divide,_1,2)等價於std::bind (&my_divide,_1,2);
  • _1表示佔位符,位於<functional>中,std::placeholders::_1;

3.2 std::bind綁定一個成員函數

 

struct Foo {
    void print_sum(int n1, int n2)
    {
        std::cout << n1+n2 << '\n';
    }
    int data = 10;
};
int main() 
{
    Foo foo;
    auto f = std::bind(&Foo::print_sum, &foo, 95, std::placeholders::_1);
    f(5); // 100
}
  • bind綁定類成員函數時,第一個參數表示對象的成員函數的指針,第二個參數表示對象的地址。
  • 必須顯示的指定&Foo::print_sum,因爲編譯器不會將對象的成員函數隱式轉換成函數指針,所以必須在Foo::print_sum前添加&;
  • 使用對象成員函數的指針時,必須要知道該指針屬於哪個對象,因此第二個參數爲對象的地址 &foo;

3.3 綁定一個引用參數

默認情況下,bind的那些不是佔位符的參數被拷貝到bind返回的可調用對象中。但是,與lambda類似,有時對有些綁定的參數希望以引用的方式傳遞,或是要綁定參數的類型無法拷貝。

 

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;

ostream & print(ostream &os, const string& s, char c)
{
    os << s << c;
    return os;
}

int main()
{
    vector<string> words{"helo", "world", "this", "is", "C++11"};
    ostringstream os;
    char c = ' ';
    for_each(words.begin(), words.end(), 
                   [&os, c](const string & s){os << s << c;} );
    cout << os.str() << endl;

    ostringstream os1;
    // ostream不能拷貝,若希望傳遞給bind一個對象,
    // 而不拷貝它,就必須使用標準庫提供的ref函數
    for_each(words.begin(), words.end(),
                   bind(print, ref(os1), _1, c));
    cout << os1.str() << endl;
}

4. 指向成員函數的指針

通過下面的例子,熟悉一下指向成員函數的指針的定義方法。

 

#include <iostream>
struct Foo {
    int value;
    void f() { std::cout << "f(" << this->value << ")\n"; }
    void g() { std::cout << "g(" << this->value << ")\n"; }
};
void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
    (foo1->*fun)();  // call fun on the object foo1
    (foo2->*fun)();  // call fun on the object foo2
}
int main() {
    Foo foo1{1};
    Foo foo2{2};
    apply(&foo1, &foo2, &Foo::f);
    apply(&foo1, &foo2, &Foo::g);
}
  • 成員函數指針的定義:void (Foo::*fun)(),調用是傳遞的實參: &Foo::f;
  • fun爲類成員函數指針,所以調用是要通過解引用的方式獲取成員函數*fun,即(foo1->*fun)();

參考



作者:georgeguo
鏈接:https://www.jianshu.com/p/f191e88dcc80
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發佈了21 篇原創文章 · 獲贊 23 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章