C++11中的std::bind
看看這段程式碼
這幾天學習Cocos2d-x,看到了以下的一段程式碼:
1 // new callbacks based on C++11 2 #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__) 3 4 #define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__) 56 #define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__) 7 8 #define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
都是定義的一些巨集,如果你看了上面的這段程式碼,覺的很簡單,那麼這篇文章你完全可以pass了;如果你對上面定義的這些巨集,完全不知道是什麼意思,那麼,這篇文章就屬於你,完全屬於你的菜。
通過這篇文章,我將帶你進入C++11中std::bind的世界,讓我們起航吧。
先來看看std::bind1st和std::bind2nd
bind是這樣一種機制,它可以預先把指定可呼叫實體的某些引數繫結到已有的變數,產生一個新的可調 用實體,這種機制在回撥函式的使用過程中也頗為有用。C++98中,有兩個函式bind1st和bind2nd,它們分別可以用來繫結functor的第 一個和第二個引數,它們都是隻可以繫結一個引數。各種限制,使得bind1st和bind2nd的可用性大大降低。
如果通過上面的內容,你還沒有明白std::bind1st和std::bind2nd到底是何方聖神到底是什麼東西,那就看這段程式碼示例:
1 #include <iostream>
2 #include <functional>
3 #include <algorithm>
4 #include <vector>
5 using namespace std;
6
7 int main()
8 {
9 vector<int> coll;
10 for (int i = 1; i <= 10; ++i)
11 {
12 coll.push_back(i);
13 }
14
15 // 查詢元素值大於10的元素的個數
16 // 也就是使得10 < elem成立的元素個數
17 int res = count_if(coll.begin(), coll.end(), bind1st(less<int>(), 10));
18 cout << res << endl;
19
20 // 查詢元素值小於10的元素的個數
21 // 也就是使得elem < 10成立的元素個數
22 res = count_if(coll.begin(), coll.end(), bind2nd(less<int>(), 10));
23 cout << res << endl;
24
25 return 0;
26 }
通過上面的程式碼明白了std::bind1st和std::bind2nd了麼?還沒有明白?好吧,我接著往細了講。
對於上面的程式碼,less<int>()
其實是一個仿函式,如果沒有std::bind1st和std::bind2nd,那麼我們可以這樣使用less<int>()
,程式碼如下:
1 less<int> functor = less<int>();
2 bool bRet = functor(10, 20); // 返回true
看到了麼?less<int>()
這個仿函式物件是需要兩個引數的,比如10<20進行比較,那麼10叫做left引數,20叫做right引數。
- 當使用
std::bind1st
的時候,就表示綁定了left引數,也就是left引數不變了,而right引數就是對應容器中的element; - 當使用
std::bind2nd
的時候,就表示綁定了right引數,也就是right引數不變了,而left引數就是對應容器中的element。
這下應該講明白了。
再來看看std::bind
C++11中提供了std::bind
。bind()函式的意義就像它的函式名一樣,是用來繫結函式呼叫的某些引數的。
bind的思想實際上是一種延遲計算的思想,將可呼叫物件儲存起來,然後在需要的時候再呼叫。而且這種繫結是非常靈活的,不論是普通函式、函式物件、還是成員函式都可以繫結,而且其引數可以支援佔位符,比如你可以這樣繫結一個二元函式auto f = bind(&func, _1, _2);
,呼叫的時候通過f(1,2)實現呼叫。
簡單的認為就是std::bind
就是std::bind1st
和std::bind2nd
的加強版。
怎麼使用std::bind
一個知識點厲不厲害,歸根到底還是要經過實踐的考驗,下面就來看看std::bind
到底怎麼用。
先看看《C++11中的std::function》中那段程式碼,std::function
可以繫結全域性函式,靜態函式,但是繫結類的成員函式時,必須要藉助std::bind
的幫忙。但是話又說回來,不借助std::bind
也是可以完成的,只需要傳一個*this變數進去就好了,比如:
1 #include <iostream>
2 #include <functional>
3 using namespace std;
4
5 class View
6 {
7 public:
8 void onClick(int x, int y)
9 {
10 cout << "X : " << x << ", Y : " << y << endl;
11 }
12 };
13
14 // 定義function型別, 三個引數
15 function<void(View, int, int)> clickCallback;
16
17 int main(int argc, const char * argv[])
18 {
19 View button;
20
21 // 指向成員函式
22 clickCallback = &View::onClick;
23
24 // 進行呼叫
25 clickCallback(button, 10, 123);
26 return 0;
27 }
再來一段示例談談怎麼使用std::bind程式碼:
1 #include <iostream>
2 #include <functional>
3 using namespace std;
4
5 int TestFunc(int a, char c, float f)
6 {
7 cout << a << endl;
8 cout << c << endl;
9 cout << f << endl;
10
11 return a;
12 }
13
14 int main()
15 {
16 auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1);
17 bindFunc1(10);
18
19 cout << "=================================\n";
20
21 auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
22 bindFunc2('B', 10);
23
24 cout << "=================================\n";
25
26 auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
27 bindFunc3(100.1, 30, 'C');
28
29 return 0;
30 }
上面這段程式碼主要說的是bind中std::placeholders的使用。 std::placeholders是一個佔位符。當使用bind生成一個新的可呼叫物件時,std::placeholders表示新的可呼叫物件的第 幾個引數和原函式的第幾個引數進行匹配,這麼說有點繞。比如:
1 auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
2
3 bindFunc3(100.1, 30, 'C');
可以看到,在bind的時候,第一個位置是TestFunc,除了這個,引數的第一個位置為佔位符std::placeholders::_2,這就表示,呼叫bindFunc3的時候,它的第二個引數和TestFunc的第一個引數匹配,以此類推。
以下是使用std::bind的一些需要注意的地方:
- bind預先繫結的引數需要傳具體的變數或值進去,對於預先繫結的引數,是pass-by-value的;
- 對於不事先繫結的引數,需要傳std::placeholders進去,從_1開始,依次遞增。placeholder是pass-by-reference的;
- bind的返回值是可呼叫實體,可以直接賦給std::function物件;
- 對於繫結的指標、引用型別的引數,使用者需要保證在可呼叫實體呼叫之前,這些引數是可用的;
- 類的this可以通過物件或者指標來繫結。
為什麼要用std::bind
當我們厭倦了使用std::bind1st
和std::bind2nd
的時候,現在有了std::bind
,你完全可以放棄使用std::bind1st
和std::bind2nd
了。std::bind
繫結的引數的個數不受限制,繫結的具體哪些引數也不受限制,由使用者指定,這個bind才是真正意義上的繫結。
在Cocos2d-x中,我們可以看到,使用std::bind
生成一個可呼叫物件,這個物件可以直接賦值給std::function
物件;在類中有一個std::function
的變數,這個std::function
由std::bind
來賦值,而std::bind
繫結的可呼叫物件可以是Lambda表示式或者類成員函式等可呼叫物件,這個是Cocos2d-x中的一般用法。
以後遇到了“奇葩”用法再繼續總結了,一次也總結不完的。
總結
又是一篇總結怎麼使用的文章,如果你覺的看的不過癮,覺的我的文章寫的不痛不癢的,還想看點更深的東西,比如std::bind
是如何實現的啊?好吧,這篇文章確實沒有說這些深層次的東西,推薦這篇文章《bind原理圖釋》,希望這篇文章能滿足你哦。