VC10_和_C0x_lambda_表達(dá)式_第1頁
VC10_和_C0x_lambda_表達(dá)式_第2頁
VC10_和_C0x_lambda_表達(dá)式_第3頁
VC10_和_C0x_lambda_表達(dá)式_第4頁
VC10_和_C0x_lambda_表達(dá)式_第5頁
已閱讀5頁,還剩10頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、.VC10和C+ 0x (1) - lambda表達(dá)式C PLUS PLUS 2011-01-14 14:15:00 閱讀84 評論0   字號:大中小 訂閱 【本文大部分內(nèi)容譯自Visual C+ Team Blog】 盡管C+社區(qū)對C+ 0x很是追捧,但是各廠商對于新標(biāo)準(zhǔn)的支持并不熱乎。盼星星盼月亮,微軟作為Windows平臺上最強(qiáng)勢的C+編譯器廠商也終于在Visual Studio 2010中開始支持C+ 0x的特性。 Lambda表達(dá)式,auto 和靜態(tài)斷言(static_assert)Visual Studio 2010中的Visua

2、l C+編譯器,即VC10, 包含了4個C+ 0x的語言特性 - lambda表達(dá)式,auto,static_assert 和 rvalue reference (右值引用). 相關(guān)鏈接:· C+0x language feature status: /JTC1/SC22/WG21/docs/papers/2008/n2705.html· C+0x library feature status: /JTC1/SC22/WG21/docs/papers/2008/n2706.html·

3、 C+0x Working Draft: /JTC1/SC22/WG21/docs/papers/2008/n2798.pdf lambdas使用過函數(shù)式編程語言(如lisp, F#)或一些動態(tài)語言(如Python,Javascript)的大俠對于lambda表達(dá)式一定不會陌生。在C+ 0x中,引入了lambda表達(dá)式來定義無名仿函數(shù)。下面是一個lambda表達(dá)式的簡單例子:/ File: meow.cpp#include <algorithm>#include <iostream>#include <ostream&

4、gt;#include <vector>using namespace std;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; return 0; C:Temp>cl /EHsc /nologo /W4 meow.cpp > NUL && m

5、eow0 1 2 3 4 5 6 7 8 9 for_each一行中,中括號稱為lambda introducer, 它告訴編譯器接下來的是一個lambda表達(dá)式;接下來(int n)是lambda表達(dá)式的參數(shù)聲明;最后大括號里邊就是“函數(shù)體”了。注意這里因為lambda表達(dá)式生成的是functor,所以“函數(shù)體”實際上是指這個functor的operator ()的調(diào)用部分。你也許會問:那么返回值呢?缺省情況下lambda表達(dá)式生成的functor調(diào)用返回類型為void。 所以,可以理解為上邊的代碼會被編譯器翻譯成如下: #include <algorithm>

6、;#include <iostream>#include <ostream>#include <vector>using namespace std;struct LambdaFunctor void operator()(int n) const cout << n << " " ;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), LambdaFun

7、ctor(); cout << endl; return 0;為了方便,以下會用"lambda返回void"的簡短表述來代替冗長啰嗦的表述:lambda表達(dá)式生成一個functor類型,這個functor類型的函數(shù)調(diào)用操作符(operator())返回的類型是void.請大家一定記?。簂ambda表達(dá)式生成了類型,并構(gòu)造該類型的實例。 下面的例子中l(wèi)ambda表達(dá)式的“函數(shù)體”包含多條語句:#include <algorithm>#include <iostream>#include <ostream>#includ

8、e <vector>using namespace std;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), (int n) cout << n; if (n % 2 = 0) cout << " even " else cout << " odd " ); cout << endl; return 0;上文提到了lambda表達(dá)式

9、缺省情況下返回void. 那么如果需要返回其他類型呢?答案是:lambda表達(dá)式的“函數(shù)體”中如果有一個return的表達(dá)式,例如 return expression; ,那么編譯器將自動推演expression的類型作為返回類型。#include <algorithm> #include <deque> #include <iostream> #include <iterator> #include <ostream> #include <vector> using namespace std; int main() &

10、#160;   vector<int> v;     for (int i = 0; i < 10; +i)         v.push_back(i);         deque<int> d;     transform(v.begin(), v.end(), front_inserter(d), (int n) return n * n * n;

11、);     for_each(d.begin(), d.end(), (int n) cout << n << " " );     cout << endl; 上例中返回值n * n * n很簡單,類型推演是顯而易見的。但是如果lambda表達(dá)式中有非常復(fù)雜的表達(dá)式時,編譯器可以無法推演出其類型,或者是推演出現(xiàn)二義性,這時候你可以顯式地指明返回值類型。如下所示: transform(v.begin(), v.end(), front_inserter(d), (in

12、t n) -> double if (n % 2 = 0) return n * n * n; else return n / 2.0; ); 黑體部分中有的“-> double”顯式地指明了lambda表達(dá)式的返回類型是double. 以上例子中的lambda都是無狀態(tài)的(stateless),不包含任何數(shù)據(jù)成員。很多時候我們需要lambda包含數(shù)據(jù)成員以保存狀態(tài),這一點可以通過“捕獲”(capturing)局部變量來實現(xiàn)。lambda表達(dá)式的導(dǎo)入符(lambda-introducer)是空的,也就是“”,表明該lambda是一個無狀態(tài)的。但是在lambda導(dǎo)

13、入符中可以指定一個“捕獲列表”(capture-list)。  int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; v.erase(remove_if(v.begin(), v.end(), x, y(int n) return x < n && n < y; ), v.end(

14、); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; 上邊的代碼中的lambda使用了局部變量x和y,將值介于x和y之間的元素從集合中刪除。程序運行示例如下 - Input: 4 70 1 2 3 4 7 8 9 上邊的代碼可以理解為:class LambdaFunctor public: LambdaFunctor(int a, int b) : m_a(a), m_b(b) bool operator()(int n) c

15、onst return m_a < n && n < m_b; private: int m_a; int m_b;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y), v.end()

16、; copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "); cout << endl; 上面代碼中很重要的一點信息是:lambda中捕獲的局部變量是以“傳值”的方式傳給匿名函數(shù)對象的。在匿名函數(shù)對象中,保存有“捕獲列表”中局部變量的拷貝。這一點使得匿名函數(shù)對象的生命周期能夠長于main中的x,y局部變量。然而這樣的傳值方式帶來幾個限制:1. lambda中的這兩個拷貝并不能被改變,因為缺省情況下函數(shù)對象的operator()是const; 2. 有的對象的拷貝操作開銷很大或者不可

17、能(例如如果上面代碼中的x, y是數(shù)據(jù)庫鏈接或者某個singleton) 3. 即使在lambda內(nèi)部修改了m_a, m_b也不能夠影響外邊main函數(shù)中的x和y  既然有了“傳值”,你一定猜到了還會有“傳引用”。bingo! 你是對的。在討論“傳引用”之前,我們先來看看另一個比較有用的東西。假設(shè)你有一大堆的局部變量需要被lambda使用,那么你的“捕獲列表”將會寫的很長,這肯定不是件愉快的事情。好在C+委員會的老頭們也想到了,C+ 0x中提供了一個省心的東西:如果捕獲列表寫成 =,表示lambda將捕獲所有的局部變量,當(dāng)然也是傳值方式。這種方式姑且被稱為“缺省捕獲”(capture

18、-default)。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; / EVIL! v.erase(remove_if(v.begin(), v.end(), =(int n) return x < n && n < y; ), v.end(); for_each(v.begin()

19、, v.end(), (int n) cout << n << " " ); cout << endl; 當(dāng)編譯器在lambda的作用范圍內(nèi)看到局部變量x, y時,它會以傳值的方式從main函數(shù)中將他們捕獲。下面我們來看如何突破前面提到的3點限制。 第一點,修改lambda表達(dá)式中的局部變量拷貝(e.g. m_a, m_b) 缺省情況下,lambda的operator ()是const 修飾的,但是你可以使用mutable關(guān)鍵字改變這一點。 int main() vector<int> v; fo

20、r (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(v.begin(), v.end(), =(int& r) mutable const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; cout << x << ",

21、" << y << endl; 代碼運行結(jié)果如下0 0 0 6 24 60 120 210 336 5041, 1 這里我們解決了第一個限制,但是卻產(chǎn)生了一個新的限制4.  lambda中對捕獲變量的修改并不會影響到main函數(shù)中的局部變量,因為lambda捕獲局部變量使用的是傳值方式 下面該“傳引用”的方式登場了,它能夠有效地解決2,3,4三個限制。 傳引用的語法為: lambda-introducer &x, &y 這里的捕獲列表應(yīng)該理解為:X& x, Y& y ; 因為我們

22、實際上是取的x,y的引用而不是地址。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(v.begin(), v.end(), &x, &y(int& r) const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << "

23、" ); cout << endl; cout << x << ", " << y << endl; 運行結(jié)果如下 - 0 0 0 6 24 60 120 210 336 5048, 9 上面代碼會被編譯器“翻譯”成:#pragma warning(push)#pragma warning(disable: 4512) / assignment operator could not be generatedclass LambdaFunctor public: LambdaFuncto

24、r(int& a, int& b) : m_a(a), m_b(b) void operator()(int& r) const const int old = r; r *= m_a * m_b; m_a = m_b; m_b = old; private: int& m_a; int& m_b;#pragma warning(pop)int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(

25、v.begin(), v.end(), LambdaFunctor(x, y); copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "); cout << endl; cout << x << ", " << y << endl; 注意:當(dāng)你使用lambda時,VC10編譯器會為lambda的定義部分自動禁用C4512警告。當(dāng)以傳引用方式捕獲局部變量時,lambda的函數(shù)對象在自己內(nèi)部以引用方式保存main函數(shù)中的

26、局部變量。當(dāng)然因為使用的是局部對象的引用,使用lambda表達(dá)式時一定要注意不能夠超出局部變量的生命周期。 和上文提高的=類似,我們可以用&來以“傳引用”的方式捕獲所有的局部變量。 到目前為止,局部變量的捕獲方式要么是“值語義”要么是“引用語義”,那么可以混合這兩種方式嗎?可以!例如:a, b, c, &d, e, &f, g,其中變量d和f是按引用語義捕獲,而a,b,c,e和g是按值語義捕獲。 另外很有用的一點是:你可以指定一個缺省捕獲(capture-default),然后重載(override)某些局部變量的捕獲方式。下邊例子中=,

27、&sum, &product告訴編譯器用值語義方式捕獲所有的局部變量,但是有兩個例外 - sum和product是按引用語義來捕獲。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int sum = 0; int product = 1; int x = 1; int y = 1; for_each(v.begin(), v.end(), =, &sum, &product(int& r) mutable sum += r; if (

28、r != 0) product *= r; const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; cout << "sum: " << sum << ", product: " << product << endl; cout << "

29、;x: " << x << ", y: " << y << endl;運行結(jié)果如下 - 0 0 0 6 24 60 120 210 336 504sum: 45, product: 362880x: 1, y: 1 再來看看下邊的代碼 - 在lambda中使用類成員變量 class Kitty public: explicit Kitty(int toys) : m_toys(toys) void meow(const vector<int>& v) const for_eac

30、h(v.begin(), v.end(), m_toys(int n) cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<int> v; for (int i = 0; i < 3; +i) v.push_back(i);

31、 Kitty k(5); k.meow(v); 不幸的是,編譯這段代碼將產(chǎn)生這樣的錯誤: error C3480: 'Kitty:m_toys': a lambda capture variable must be from an enclosing function scope為什么呢?lambda表達(dá)式能夠讓你不活局部變量,但是類的數(shù)據(jù)成員并不是局部變量。解決方案呢?別著急。lambda為捕獲類的數(shù)據(jù)成員大開方便之門,你可以捕獲this指針。class Kitty public: explicit Kitty(int toys) : m_toys(toy

32、s) void meow(const vector<int>& v) const for_each(v.begin(), v.end(), this(int n) cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<

33、int> v; for (int i = 0; i < 3; +i) v.push_back(i); Kitty k(5); k.meow(v);運行結(jié)果 - If you gave me 0 toys, I would have 5 toys total.If you gave me 1 toys, I would have 6 toys total.If you gave me 2 toys, I would have 7 toys total. 當(dāng)lambda表達(dá)式捕獲“this”時,編譯器看到m_toys后會在this所指向?qū)ο蟮姆秶鷥?nèi)進(jìn)行名字查找,m_toys被

34、隱式地推演為this->m_toys。當(dāng)然你也可以讓編譯器省省力氣。顯式地在捕獲列表中使用 this->m_toys。lambda比較智能,你也可以隱式地捕獲this指針。如下所示:class Kitty public: explicit Kitty(int toys) : m_toys(toys) void meow(const vector<int>& v) const for_each(v.begin(), v.end(), =(int n) cout << "If you gave me " << n <

35、< " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<int> v; for (int i = 0; i < 3; +i) v.push_back(i); Kitty k(5); k.meow(v);運行結(jié)果:If you gave me 0 toys, I would have 5 toys total.If you gave me 1 toys, I would have 6 toys total.If you gave me 2 toys, I would have 7 toys total. 注意你也可以在上面代碼中用 &,但是結(jié)果是一樣的 - this指針永遠(yuǎn)是按值語義被傳遞(捕獲)的。你也不能夠使用 &this,呵呵。如果你的lambda表達(dá)式是沒有參數(shù)的,那么lambda表達(dá)式的導(dǎo)入符后邊的括號()也可以省掉。例如: int main() vector<int> v; int i

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論