C++11 中的std::function和std::bind詳解
1. 可調(diào)用對象
可調(diào)用對象有一下幾種定義:
- 是一個函數(shù)指針,參考 C++ 函數(shù)指針和函數(shù)類型;
- 是一個具有operator()成員函數(shù)的類的對象;
- 可被轉(zhuǎn)換成函數(shù)指針的類對象;
- 一個類成員函數(shù)指針;
C++中可調(diào)用對象的雖然都有一個比較統(tǒng)一的操作形式,但是定義方法五花八門,這樣就導(dǎo)致使用統(tǒng)一的方式保存可調(diào)用對象或者傳遞可調(diào)用對象時,會十分繁瑣。C++11中提供了std::function和std::bind統(tǒng)一了可調(diào)用對象的各種操作。
不同類型可能具有相同的調(diào)用形式,如:
// 普通函數(shù) int add(int a, int b){return a+b;} // lambda表達式 auto mod = [](int a, int b){ return a % b;} // 函數(shù)對象類 struct divide{ int operator()(int denominator, int divisor){ return denominator/divisor; } };
上述三種可調(diào)用對象雖然類型不同,但是共享了一種調(diào)用形式:
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 是一個可調(diào)用對象包裝器,是一個類模板,可以容納除了類成員函數(shù)指針之外的所有可調(diào)用對象,它可以用統(tǒng)一的方式處理函數(shù)、函數(shù)對象、函數(shù)指針,并允許保存和延遲它們的執(zhí)行。
- 定義格式:std::function<函數(shù)類型>。
- std::function可以取代函數(shù)指針的作用,因為它可以延遲函數(shù)的執(zhí)行,特別適合作為回調(diào)函數(shù)使用。它比普通函數(shù)指針更加的靈活和便利。
3. std::bind
可將std::bind函數(shù)看作一個通用的函數(shù)適配器,它接受一個可調(diào)用對象,生成一個新的可調(diào)用對象來“適應(yīng)”原對象的參數(shù)列表。
std::bind將可調(diào)用對象與其參數(shù)一起進行綁定,綁定后的結(jié)果可以使用std::function保存。std::bind主要有以下兩個作用:
- 將可調(diào)用對象和其參數(shù)綁定成一個防函數(shù);
- 只綁定部分參數(shù),減少可調(diào)用對象傳入的參數(shù)。
3.1 std::bind綁定普通函數(shù)
- bind的第一個參數(shù)是函數(shù)名,普通函數(shù)做實參時,會隱式轉(zhuǎn)換成函數(shù)指針。因此std::bind (my_divide,_1,2)等價于std::bind (&my_divide,_1,2);
- _1表示占位符,位于<functional>中,std::placeholders::_1;
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
3.2 std::bind綁定一個成員函數(shù)
- bind綁定類成員函數(shù)時,第一個參數(shù)表示對象的成員函數(shù)的指針,第二個參數(shù)表示對象的地址。
- 必須顯示的指定&Foo::print_sum,因為編譯器不會將對象的成員函數(shù)隱式轉(zhuǎn)換成函數(shù)指針,所以必須在Foo::print_sum前添加&;
- 使用對象成員函數(shù)的指針時,必須要知道該指針屬于哪個對象,因此第二個參數(shù)為對象的地址 &foo;
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 }
3.3 綁定一個引用參數(shù)
默認情況下,bind的那些不是占位符的參數(shù)被拷貝到bind返回的可調(diào)用對象中。但是,與lambda類似,有時對有些綁定的參數(shù)希望以引用的方式傳遞,或是要綁定參數(shù)的類型無法拷貝。
#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函數(shù) for_each(words.begin(), words.end(), bind(print, ref(os1), _1, c)); cout << os1.str() << endl; }
4. 指向成員函數(shù)的指針
通過下面的例子,熟悉一下指向成員函數(shù)的指針的定義方法。
- 成員函數(shù)指針的定義:void (Foo::*fun)(),調(diào)用是傳遞的實參: &Foo::f;
- fun為類成員函數(shù)指針,所以調(diào)用是要通過解引用的方式獲取成員函數(shù)*fun,即(foo1->*fun)();
#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); }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C++使用Kruskal和Prim算法實現(xiàn)最小生成樹
這篇文章主要介紹了C++使用Kruskal和Prim算法實現(xiàn)最小生成樹,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01C++實現(xiàn)將一個字符串中的字符替換成另一個字符串的方法
這篇文章主要介紹了C++實現(xiàn)將一個字符串中的字符替換成另一個字符串的方法,需要考慮的情況比較全面,有不錯的借鑒價值,需要的朋友可以參考下2014-09-09C++實現(xiàn)LeetCode(兩個有序數(shù)組的中位數(shù))
這篇文章主要介紹了C++實現(xiàn)LeetCode(兩個有序數(shù)組的中位數(shù)),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07C++ virtual destructor虛擬析構(gòu)函數(shù)
C++中基類采用virtual虛析構(gòu)函數(shù)是為了防止內(nèi)存泄漏。具體地說,如果派生類中申請了內(nèi)存空間,并在其析構(gòu)函數(shù)中對這些內(nèi)存空間進行釋放,今天通過本文給大家介紹C++ virtual destructor虛擬析構(gòu)函數(shù)的相關(guān)知識,感興趣的朋友一起看看吧2021-05-05C語言strlen函數(shù)實現(xiàn)讀取字符串長度詳解
這篇文章主要介紹了用C語言的strlen函數(shù)來實現(xiàn)讀取字符串長度的過程,strlen所作的是一個計數(shù)器的工作,它從內(nèi)存的某個位置開始掃描,直到碰到第一個字符串結(jié)束符'\0'為止2022-04-04