C++11 std::function, std::bind, std::ref, std::cref
C++11 std::function, std::bind, std::ref, std::cref
轉(zhuǎn)自:http://www.jellythink.com/
std::function
看看這段代碼
先來(lái)看看下面這兩行代碼:
std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed; std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased;這兩行代碼是從Cocos2d-x中摘出來(lái)的,重點(diǎn)是這兩行代碼的定義啊。std::function這是什么東西?如果你對(duì)上述兩行代碼表示毫無(wú)壓力,那就不妨再看看本文,就當(dāng)溫故而知新吧。
std::function介紹
類(lèi)模版std::function是一種通用、多態(tài)的函數(shù)封裝。std::function的實(shí)例可以對(duì)任何可以調(diào)用的目標(biāo)實(shí)體進(jìn)行存儲(chǔ)、復(fù)制、和調(diào)用操作,這些目標(biāo)實(shí)體包括普通函數(shù)、Lambda表達(dá)式、函數(shù)指針、以及其它函數(shù)對(duì)象等。std::function對(duì)象是對(duì)C++中現(xiàn)有的可調(diào)用實(shí)體的一種類(lèi)型安全的包裹(我們知道像函數(shù)指針這類(lèi)可調(diào)用實(shí)體,是類(lèi)型不安全的)。
通常std::function是一個(gè)函數(shù)對(duì)象類(lèi),它包裝其它任意的函數(shù)對(duì)象,被包裝的函數(shù)對(duì)象具有類(lèi)型為T(mén)1, …,TN的N個(gè)參數(shù),并且返回一個(gè)可轉(zhuǎn)換到R類(lèi)型的值。std::function使用 模板轉(zhuǎn)換構(gòu)造函數(shù)接收被包裝的函數(shù)對(duì)象;特別是,閉包類(lèi)型可以隱式地轉(zhuǎn)換為std::function。
最簡(jiǎn)單的理解就是:
通過(guò)std::function對(duì)C++中各種可調(diào)用實(shí)體(普通函數(shù)、Lambda表達(dá)式、函數(shù)指針、以及其它函數(shù)對(duì)象等)的封裝,形成一個(gè)新的可調(diào)用的std::function對(duì)象;讓我們不再糾結(jié)那么多的可調(diào)用實(shí)體。一切變的簡(jiǎn)單粗暴。
怎么使用std::function
使用std::function的感覺(jué)就是“萬(wàn)眾歸一”,下面就通過(guò)實(shí)際的代碼例子,看看究竟怎么使用std::function。會(huì)使用了才是王道。
#include <functional> #include <iostream> using namespace std;std::function< int(int)> Functional;// 普通函數(shù) int TestFunc(int a) {return a; }// Lambda表達(dá)式 auto lambda = [](int a)->int{ return a; };// 仿函數(shù)(functor) class Functor { public:int operator()(int a){return a;} };// 1.類(lèi)成員函數(shù) // 2.類(lèi)靜態(tài)函數(shù) class TestClass { public:int ClassMember(int a) { return a; }static int StaticMember(int a) { return a; } };int main() {// 普通函數(shù)Functional = TestFunc;int result = Functional(10);cout << "普通函數(shù):"<< result << endl;// Lambda表達(dá)式Functional = lambda;result = Functional(20);cout << "Lambda表達(dá)式:"<< result << endl;// 仿函數(shù)Functor testFunctor;Functional = testFunctor;result = Functional(30);cout << "仿函數(shù):"<< result << endl;// 類(lèi)成員函數(shù)TestClass testObj;Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);result = Functional(40);cout << "類(lèi)成員函數(shù):"<< result << endl;// 類(lèi)靜態(tài)函數(shù)Functional = TestClass::StaticMember;result = Functional(50);cout << "類(lèi)靜態(tài)函數(shù):"<< result << endl;return 0; }對(duì)于各個(gè)可調(diào)用實(shí)體轉(zhuǎn)換成std::function類(lèi)型的對(duì)象,上面的代碼都有,運(yùn)行一下代碼,閱讀一下上面那段簡(jiǎn)單的代碼。總結(jié)了簡(jiǎn)單的用法以后,來(lái)看看一些需要注意的事項(xiàng):
-
關(guān)于可調(diào)用實(shí)體轉(zhuǎn)換為
std::function對(duì)象需要遵守以下兩條原則:
- 轉(zhuǎn)換后的std::function對(duì)象的參數(shù)能轉(zhuǎn)換為可調(diào)用實(shí)體的參數(shù);
- 可調(diào)用實(shí)體的返回值能轉(zhuǎn)換為std::function對(duì)象的返回值。
-
std::function對(duì)象最大的用處就是在實(shí)現(xiàn)函數(shù)回調(diào),使用者需要注意,它不能被用來(lái)檢查相等或者不相等,但是可以與NULL或者nullptr進(jìn)行比較。
為什么要用std::function
好用并實(shí)用的東西才會(huì)加入標(biāo)準(zhǔn)的。因?yàn)楹糜?#xff0c;實(shí)用,我們才在項(xiàng)目中使用它。std::function實(shí)現(xiàn)了一套類(lèi)型消除機(jī)制,可以統(tǒng)一處理不同的函數(shù)對(duì)象類(lèi)型。以前我們使用函數(shù)指針來(lái)完成這些;現(xiàn)在我們可以使用更安全的std::function來(lái)完成這些任務(wù)。
還有為什么?我也不知道還有為什么?等以后發(fā)現(xiàn)了更好的實(shí)際應(yīng)用實(shí)例再回來(lái)說(shuō)為什么吧。
std::bind
看看這段代碼
這幾天學(xué)習(xí)Cocos2d-x,看到了以下的一段代碼:
// 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__)都是定義的一些宏,如果你看了上面的這段代碼,覺(jué)的很簡(jiǎn)單,那么這篇文章你完全可以pass了;如果你對(duì)上面定義的這些宏,完全不知道是什么意思,那么,這篇文章就屬于你,完全屬于你的菜。
通過(guò)這篇文章,我將帶你進(jìn)入C++11中std::bind的世界,讓我們起航吧。
先來(lái)看看std::bind1st和std::bind2nd
bind是這樣一種機(jī)制,它可以預(yù)先把指定可調(diào)用實(shí)體的某些參數(shù)綁定到已有的變量,產(chǎn)生一個(gè)新的可調(diào) 用實(shí)體,這種機(jī)制在回調(diào)函數(shù)的使用過(guò)程中也頗為有用。C++98中,有兩個(gè)函數(shù)bind1st和bind2nd,它們分別可以用來(lái)綁定functor的第 一個(gè)和第二個(gè)參數(shù),它們都是只可以綁定一個(gè)參數(shù)。各種限制,使得bind1st和bind2nd的可用性大大降低。
如果通過(guò)上面的內(nèi)容,你還沒(méi)有明白std::bind1st和std::bind2nd到底是何方圣神到底是什么東西,那就看這段代碼示例:
#include <iostream> #include <functional> #include <algorithm> #include <vector> using namespace std;int main() {vector<int> coll;for (int i = 1; i <= 10; ++i){coll.push_back(i);}// 查找元素值大于10的元素的個(gè)數(shù)// 也就是使得10 < elem成立的元素個(gè)數(shù) int res = count_if(coll.begin(), coll.end(), bind1st(less<int>(), 10));cout << res << endl;// 查找元素值小于10的元素的個(gè)數(shù)// 也就是使得elem < 10成立的元素個(gè)數(shù) res = count_if(coll.begin(), coll.end(), bind2nd(less<int>(), 10));cout << res << endl;return 0; }通過(guò)上面的代碼明白了std::bind1st和std::bind2nd了么?還沒(méi)有明白?好吧,我接著往細(xì)了講。
對(duì)于上面的代碼,less<int>()其實(shí)是一個(gè)仿函數(shù),如果沒(méi)有std::bind1st和std::bind2nd,那么我們可以這樣使用less<int>(),代碼如下:
less<int> functor = less<int>(); bool bRet = functor(10, 20); // 返回true看到了么?less<int>()這個(gè)仿函數(shù)對(duì)象是需要兩個(gè)參數(shù)的,比如10<20進(jìn)行比較,那么10叫做left參數(shù),20叫做right參數(shù)。
- 當(dāng)使用std::bind1st的時(shí)候,就表示綁定了left參數(shù),也就是left參數(shù)不變了,而right參數(shù)就是對(duì)應(yīng)容器中的element;
- 當(dāng)使用std::bind2nd的時(shí)候,就表示綁定了right參數(shù),也就是right參數(shù)不變了,而left參數(shù)就是對(duì)應(yīng)容器中的element。
這下應(yīng)該講明白了。
再來(lái)看看std::bind
C++11中提供了std::bind。bind()函數(shù)的意義就像它的函數(shù)名一樣,是用來(lái)綁定函數(shù)調(diào)用的某些參數(shù)的。
bind的思想實(shí)際上是一種延遲計(jì)算的思想,將可調(diào)用對(duì)象保存起來(lái),然后在需要的時(shí)候再調(diào)用。而且這種綁定是非常靈活的,不論是普通函數(shù)、函數(shù)對(duì)象、還是成員函數(shù)都可以綁定,而且其參數(shù)可以支持占位符,比如你可以這樣綁定一個(gè)二元函數(shù)auto f = bind(&func, _1, _2);,調(diào)用的時(shí)候通過(guò)f(1,2)實(shí)現(xiàn)調(diào)用。
簡(jiǎn)單的認(rèn)為就是std::bind就是std::bind1st和std::bind2nd的加強(qiáng)版。
怎么使用std::bind
一個(gè)知識(shí)點(diǎn)厲不厲害,歸根到底還是要經(jīng)過(guò)實(shí)踐的考驗(yàn),下面就來(lái)看看std::bind到底怎么用。
先看看《C++11中的std::function》中那段代碼,std::function可以綁定全局函數(shù),靜態(tài)函數(shù),但是綁定類(lèi)的成員函數(shù)時(shí),必須要借助std::bind的幫忙。但是話(huà)又說(shuō)回來(lái),不借助std::bind也是可以完成的,只需要傳一個(gè)*this變量進(jìn)去就好了,比如:
#include <iostream> #include <functional> using namespace std;class View { public:void onClick(int x, int y){cout << "X : " << x << ", Y : " << y << endl;} };// 定義function類(lèi)型, 三個(gè)參數(shù) function<void(View, int, int)> clickCallback;int main(int argc, const char * argv[]) {View button;// 指向成員函數(shù)clickCallback = &View::onClick;// 進(jìn)行調(diào)用clickCallback(button, 10, 123);return 0; }再來(lái)一段示例談?wù)勗趺词褂胹td::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; }上面這段代碼主要說(shuō)的是bind中std::placeholders的使用。 std::placeholders是一個(gè)占位符。當(dāng)使用bind生成一個(gè)新的可調(diào)用對(duì)象時(shí),std::placeholders表示新的可調(diào)用對(duì)象的第 幾個(gè)參數(shù)和原函數(shù)的第幾個(gè)參數(shù)進(jìn)行匹配,這么說(shuō)有點(diǎn)繞。比如:
auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);bindFunc3(100.1, 30, 'C');可以看到,在bind的時(shí)候,第一個(gè)位置是TestFunc,除了這個(gè),參數(shù)的第一個(gè)位置為占位符std::placeholders::_2,這就表示,調(diào)用bindFunc3的時(shí)候,它的第二個(gè)參數(shù)和TestFunc的第一個(gè)參數(shù)匹配,以此類(lèi)推。
以下是使用std::bind的一些需要注意的地方:
- bind預(yù)先綁定的參數(shù)需要傳具體的變量或值進(jìn)去,對(duì)于預(yù)先綁定的參數(shù),是pass-by-value的;
- 對(duì)于不事先綁定的參數(shù),需要傳std::placeholders進(jìn)去,從_1開(kāi)始,依次遞增。placeholder是pass-by-reference的;
- bind的返回值是可調(diào)用實(shí)體,可以直接賦給std::function對(duì)象;
- 對(duì)于綁定的指針、引用類(lèi)型的參數(shù),使用者需要保證在可調(diào)用實(shí)體調(diào)用之前,這些參數(shù)是可用的;
- 類(lèi)的this可以通過(guò)對(duì)象或者指針來(lái)綁定。
為什么要使用std::bind
當(dāng)我們厭倦了使用std::bind1st和std::bind2nd的時(shí)候,現(xiàn)在有了std::bind,你完全可以放棄使用std::bind1st和std::bind2nd了。std::bind綁定的參數(shù)的個(gè)數(shù)不受限制,綁定的具體哪些參數(shù)也不受限制,由用戶(hù)指定,這個(gè)bind才是真正意義上的綁定。
在Cocos2d-x中,我們可以看到,使用std::bind生成一個(gè)可調(diào)用對(duì)象,這個(gè)對(duì)象可以直接賦值給std::function對(duì)象;在類(lèi)中有一個(gè)std::function的變量,這個(gè)std::function由std::bind來(lái)賦值,而std::bind綁定的可調(diào)用對(duì)象可以是Lambda表達(dá)式或者類(lèi)成員函數(shù)等可調(diào)用對(duì)象,這個(gè)是Cocos2d-x中的一般用法。
以后遇到了“奇葩”用法再繼續(xù)總結(jié)了,一次也總結(jié)不完的。
又是一篇總結(jié)怎么使用的文章,如果你覺(jué)的看的不過(guò)癮,覺(jué)的我的文章寫(xiě)的不痛不癢的,還想看點(diǎn)更深的東西,比如std::bind是如何實(shí)現(xiàn)的啊?好吧,這篇文章確實(shí)沒(méi)有說(shuō)這些深層次的東西,推薦這篇文章《bind原理圖釋》,希望這篇文章能滿(mǎn)足你哦。
std::ref, std::cref
C++本身有引用(&),為什么C++11又引入了std::ref(或者std::cref)?
主要是考慮函數(shù)式編程(**如std::bind、std::thread)**在使用時(shí),是對(duì)參數(shù)直接拷貝,而不是引用。std::bind()是一個(gè)函數(shù)模板,它的原理是根據(jù)已有的模板,生成一個(gè)函數(shù),但是由于bind()不知道生成的函數(shù)執(zhí)行的時(shí)候,傳遞進(jìn)來(lái)的參數(shù)是否還有效。所以它選擇參數(shù)值傳遞而不是引用傳遞。如果想引用傳遞,std::ref和std::cref就派上用場(chǎng)了。如下例子:
#include <functional> #include <iostream>void f(int& n1, int& n2, const int& n3) {std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';++n1; // increments the copy of n1 stored in the function object++n2; // increments the main()'s n2// ++n3; // compile error }int main() {int n1 = 1, n2 = 2, n3 = 3;std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));n1 = 10;n2 = 11;n3 = 12;std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';bound_f();std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; }輸出:
Before function: 10 11 12 In function: 1 11 12 After function: 10 12 12上述代碼在執(zhí)行std::bind后,在函數(shù)f()中n1的值仍然是1,n2和n3改成了修改的值。說(shuō)明std::bind使用的是參數(shù)的拷貝而不是引用。具體為什么std::bind不使用引用,可能確實(shí)有一些需求,使得C++11的設(shè)計(jì)者認(rèn)為默認(rèn)應(yīng)該采用拷貝,如果使用者有需求,加上std::ref即可。
總結(jié)
以上是生活随笔為你收集整理的C++11 std::function, std::bind, std::ref, std::cref的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: pm2 start 带参数_3款有海景天
- 下一篇: 相互保可以多少大病