C++11中的std::bind
最近在看看cocos2dx的源代碼,發現了cocos2dx 3.0相對於2.0改動了很多,最大的改變就是大量的使用了C++11的特性,比如auto
等。其中有一個關於回調函數綁定的宏定義就使用了std::bind
特性
// 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__)
非常簡練的宏定義,對於沒有接觸過C++11的朋友來說,真的是一頭霧水,這貨是什么,真的是C++嗎?所以筆者就寫了這篇文章來講解。
C++發展的歷史
C++經過多年發展,真正正式公布出的標准只有三個C++98,C++03,C++11。其中C++03只是C++98的小幅度修訂,在筆者看來,C++發展的歷史,就是一個不斷吸收新特性的歷史。從最早的面向過程,面向對象,模板編程,到現在的函數式編程,C++一直都是在吸收新特性。lambda
就是函數式編程閉包的特性。
何為bind
bind是這樣一種機制,它可以預先把指定可調用實體的某些參數綁定到已有的變量,產生一個新的可調 用實體,這種機制在回調函數的使用過程中也頗為有用。其實最早在C++98的時候,就已經有了std::bind1st和std::bind2nd分別用來綁定functor的兩個參數,具體代碼就不演示了,查查資料就知道了。這個特性在當時並沒有引起太多的重視,可以說是食之無味。
C++11中提供了std::bind
,可以說是一種飛躍的提升,bind本身是一種延遲計算的思想,它本身可以綁定普通函數、全局函數、靜態函數、類靜態函數甚至是類成員函數。
#include <iostream> #include <functional> using namespace std; int TestFunc(int a, char c, float f) { cout << a << endl; cout << c << endl; cout << f << endl; return a; } int main() { auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1); bindFunc1(10); cout << "=================================\n"; auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1); bindFunc2('B', 10); cout << "=================================\n"; auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1); bindFunc3(100.1, 30, 'C'); return 0; }
從上面的代碼可以看到,bind能夠在綁定時候就同時綁定一部分參數,未提供的參數則使用占位符表示,然后在運行時傳入實際的參數值。
PS:綁定的參數將會以值傳遞的方式傳遞給具體函數,占位符將會以引用傳遞。
眾所周知,靜態成員函數其實可以看做是全局函數,而非靜態成員函數則需要傳遞this指針作為第一個參數,所以std::bind
能很容易地綁定成員函數。
cocos2dx中的CC_CALLBACK
系列的宏其實就是一種封裝好的std::bind
,它默認認為綁定的就是成員函數,幫助開發者簡化了代碼。
總結
bind
最終將會生成一個可調用對象,這個對象可以直接賦值給std::function
對象,而std::bind綁定的可調用對象可以是Lambda表達式或者類成員函數等可調用對象,這個是cocos2dx中的一般用法。它能隨意綁定任何函數,將所有的函數都能統一到std::function
example:
#include <iostream> #include <typeinfo> #include <functional> #include <string.h> #include <iostream> #include <typeinfo> #include <functional> #include <string.h> using namespace std; int add(int a, int b, int c) { return a+b+c; } class Utils{ public: Utils(const char* name){ strcpy(_name, name); } void SayHello(const char* name) const{ std::cout<<_name<<" say : hello "<<name<<endl; } static int getId(){ return 1001; } private: char _name[32]; }; int main() { auto add2 = std::bind(add, std::placeholders::_1, 1, 2); int i=add2(5); std::cout<<i<<std::endl; std::cout<<typeid(add2).name()<<endl; cout<<"------------------------------------"<<endl; Utils util("xiaoming"); //綁定類成員 auto sayHello = std::bind(&Utils::SayHello, util, std::placeholders::_1); sayHello("xiaodonmg"); auto SayHelloKit = std::bind(&Utils::SayHello, util, "kit"); SayHelloKit(); //綁定靜態成員 auto getId = std::bind(&Utils::getId); cout<<getId()<<endl; return 0; }