C++11系列-lambda函式
原文地址:http://towriting.com/blog/2013/08/11/lambda-closures/
C++11一個最激動人心的特性是支援建立lambda函式(有時稱為閉包)。這意味著什麼?一個Lambda函式是一個可以內聯寫在你程式碼中的函式(通常也會傳遞給另外的函式,類似於仿函式或函式指標)。使用Lambda,建立機動函式會更簡單,而以前你必須建立一個有名函式。在這篇文章中,我先用一些例子解釋為什麼lambda很酷,然後我會講解可能會用到的關於lambda的所有細節。
為什麼Lambda很酷
想象你有一個地址簿類,並且你想要提供一個可供檢索的函式。你可能會提供一個簡單的函式,接受一個字串然後返回滿足所有字串的地址。有時有些使用者可能希望這樣。不過假如他們只是想檢索域名或者檢索使用者名稱並且忽略域名結果;或者檢索出現在其他列表中的所有Email地址。這裡可能有許多可能的檢索方式。除了類中整合所有這些搜尋選項,提供一個通用的查詢方法,這個方法接受一個查詢規則的函式,這樣不是更好些嗎?讓我們叫這個函式findMatchingAddresses,它接受一個函式或仿函式物件。
|
任何人可以傳遞一個包含地址查詢邏輯的函式給findMatchingAddresses。假如這個函式返回真,則得到相應的地址,地址將被返回。這種方式在以前的C++中一樣支援,不過卻遭遇一個致命缺陷:建立函式非常不方便。你必須先在其他地方定義好函式,你才能使用它。這就是Lambda出現的原因。
基本Lambda語法
在我們解決這個問題之前,讓我們看一下真實的lambda基本語法。
|
好,你找到lambda了嗎?它以[]開始。這個標識,叫做捕獲指定器,它告訴編譯器我們要建立一個lambda表示式。你將看到[](或者裡面有變數)在每一個lambda函式的開始。
接著,像其他函式一樣,我們需要一個引數列表:()。返回值呢?答案是我們不需要指定。在C++11中,假如編譯器可以推導lambda函式的返回值,它將幫你做這件事而不需你顯式指定。在這個例子裡,編譯器知道函式沒有返回值。我們只是有一個列印“hello world”的函式體。這一行事實上不會觸發關於列印的任何事:我們僅僅是建立了一個函式在這裡。基本上相當於定義了一個普通函式。
我們在下面一行呼叫了這個lambda函式:func(),像呼叫其它普通函式一樣。順便看到,配合auto做這些事情是多麼簡單!你不用擔心函式指標的醜陋語法。
在我們的例子中應用Lambda
讓我們看看怎樣將lambda應用到我們地址簿例子裡,首先我們建立一個查詢包含“.org”的email地址的簡單函式。
|
再一次,我們以捕獲指示符[]開始,但這一次我們有一個引數:地址,並且我們檢測地址中是否含有“.org”。再一次說明,lambda的函式體並沒有在這裡執行;它只會在函式findMatchingAddresses內,當函式變數被使用時,lambda中的程式碼才會執行。換句話說,findMatchingAddresses的每個迴圈中會呼叫lambda函式,並傳給它一個地址作為引數,然後這個函式檢測地址是否包含“.org”。
變數捕獲
雖然這些簡單的lambda用法也不錯,但變數捕獲才是成就lambda卓越的祕方。假如你想建立一個查詢包含指定名字的短函式。如果可以寫出這樣的程式碼是不是非常不錯?
|
可以證明示例程式碼是合法的,並且它展現了lambda函式的價值。我們可以獲取宣告在lambda函式之外的變數(name),並在lambda之內使用。當findMatchingAddresses呼叫我們的lambda函式,函式體會被執行,當addr.find被呼叫,它處理使用者程式碼傳進的name。為了使這可以執行的唯一要做的事是捕獲變數。我用[&]捕獲指示做這件事,而不是用[]。[]是告訴編譯器不捕獲任何變數,而[&]是告訴編譯器去捕獲變數。
是不是不可思議?我們建立了一個簡單的可以捕獲變數的函式,並將它傳給find函式,所有這些只用了幾行程式碼。如果不用C++11實現這些,我們需要建立一個仿函式或者給AddressBook類新增一個特殊方法。用C++11,我們可以輕易實現一個簡單的介面函式,但支援各種檢索的功能。
只是好玩,我們想查詢email地址小於某個特殊長度的地址。我們可以再一次輕鬆實現:
|