C++ 理解函數對象與lambda表達式

參考《21天學通C++》第21與第22章節,對函數對象進行介紹,同時經過lambda表達式這一匿名函數對象的簡潔方式加深對函數對象的理解。本篇博文的主要內容是:css

(1) 函數對象的概念;html

(2) 將函數對象用做謂詞;算法

(3) 如何使用函數對象實現一元、二元謂詞;express

(4) 如何編寫lambda表達式;函數

(5) 如何將lambda表達式用做謂詞;spa

(6) 如何編寫可存儲和可操做狀態的lambda表達式。指針

1、 函數對象

1. 函數對象的概念和種類

函數對象是C++實體,從概念上講,函數對象是用做函數的對象;從實現上說,函數對象是實現了operator()的類的對象。雖然函數和函數指針也可視爲函數對象,可是吸納了operator()的類的對象才能保存狀態,才能用於標準模板庫算法。C++經常使用於STL算法的函數對象可分爲下面兩種類型:(1) 一元函數:接受一個參數的函數,若是一元函數返回一個布爾值,稱之爲謂詞;(2) 二元函數:接受兩個參數的函數,若返回bool,則稱爲二元謂詞函數。返回bool值得函數對象經常用來進行判斷的算法,組合兩個函數對象的函數對象稱之爲自適應函數對象。code

2. 函數對象的典型用途

(1) 一元函數的用途

對於STL算法std::for_each()接受三個參數,第一個參數指定範圍的起點,第二個參數指定範圍的終點,第三個參數是要對指定範圍內的每一個元素調用的函數。這個函數就能夠經過一元謂詞實現,舉例以下:orm

template <typename elementType>htm

struct DisplayElement

{

void operator () (const elementType& element) const

{

cout<< element << ' ';

}

};

vector<int> vectorElement;

for_each(vectorElement.begin(), vectorElement.end(), DisplayElement<int> ());

這樣就可使用STL算法for_each來對指定範圍內的數據執行相同的函數方法。這裏也可使用匿名函數對象,即lambda表達式,以下所示,將上面的for_each改造爲相同的功能:

for_each(vectorElement.begin(), vectorElement.end(), [](int& element) { cout << element << ' ';});

若是可以使用結構的對象來存儲信息,則使用在結構中實現的函數對象的優勢將顯現出來。

(2) 一元謂詞的用途

返回布爾值的一元函數就是謂詞,這種函數可用於STL算法的判斷。

一元謂詞被大量用於STL算法中,例如,std::partition算法使用一元謂詞來劃分範圍,stable_partition算法也使用一元謂詞劃分範圍,但保持元素的相對順序不變。諸如std::find_if等查找函數以及std::remove_if等刪除元素的函數也使用一元謂詞,其中std::remove_if刪除指定範圍內知足謂詞條件的元素。

(3) 二元函數的用途

若是函數f(x,y)根據輸入參數返回一個值,它將頗有用。這種二元函數可用於對兩個操做數執行運算,如加減乘除等。一樣實現最重要的operator()。在std::transform等算法中,可以使用二元乘積函數計算兩個容器內容的點乘。

template <typename elementType>

class Multiply

{

public:

elementType operator () (const elmentType& elem1,const elmentType& elem2)

{ return (elem1 * elem2);}

}

vector<int> vecMultiplicand, vecMultiplier;

vector<int> vecResult;

transform(vecMultiplicand.begin(), vecMultiplicand.end(), vecMultiplier.begin(), vecResult.begin(), Multiply <int>());

transform將兩個範圍的內容相乘,並將結果存儲在第三個範圍中。這裏,這三個範圍分別存儲在類型std::vector的vecMultiplicand、vecMultiplier和vecResult中。乘法運算是經過調用二元函數Multiply::operator ()執行的,對源範圍和目標範圍內的每一個元素都調用了該函數。operator()的返回值保存在vecResult中。

(4) 二元謂詞的用途

接受兩個參數並返回一個布爾值的函數是二元謂詞,這種函數用於諸如std::sort等STL函數中。使用排序二元謂詞實現排序,經過實現operator()來動態排序。

不少STL算法都使用二元謂詞,好比刪除相鄰重複元素的std::unique()、排序算法sort、排序並保持相對順序的std::stable_sort以及兩個範圍進行操做的std::transform。

(5) 總結

在結構或類中實現函數對象時,它將比簡單函數有用的多,由於它也可用於存儲與狀態相關的信息。謂詞是一類特殊的函數對象。


2、 C++ lambda表達式

1. lambda表示概念

可將lambda表達式視爲包含公有operator()的匿名結構(或類),從這種意義上說,lambda表達式屬於函數對象。從上面所講到的進行分析:

for_each(vectorElement.begin(), vectorElement.end(), [](int& element) { cout << element << ' ';});

編譯器加到下述lambda表達式時:[](int& element) { cout << element << ' ';}自動將其展開爲相似結構DisplayElement<int>的表示:

struct DisplayElement

{

void operator () (const int& element) const

{

cout<< element << ' ';

}

};

2. 如何定義lambda表達式

以方括號[]打頭,告訴編譯器,接下來是一個lambda表達式,方括號後面是一個參數列表,該參數列表與不使用lambda表達式時提供給operator()參數列表相同。

3. 一元函數對應的lambda表達式

與一元operator(Type)對應的lambda表達式接收一個參數,定義以下:

[](Type paramName){ // lambda expression code here}也可用引用來傳參:[](Type& paramName){ // lambda expression code here}。

4. 一元謂詞對應的lambda表達式

謂詞可幫助作出決策,一元謂詞是返回bool類型的一元表達式。舉例以下:

[](int& Num){return ((Num % 2) == 0);}

在這裏,返回值的性質是爲了讓編譯器知道該lambda表達式的返回類型爲bool。在算法中可將lambda表達式用於一元謂詞,如可在find_if中使用上述lambda表達式找出集合中的偶數。若是謂詞返回true,find_if將返回一個指向相應元素的迭代器,指出找到一個知足條件的元素。

5. 經過捕獲列表接受狀態變量的lambda表達式

對於上面的一元謂詞實如今整數能被2整除時返回true,若是讓它更通用,在數字能被用戶指定除數整除時返回true,可採用狀態變量:

int Divisor = 2;

[Divisor ](int& Num){return ((Num % Divisor ) == 0);}

一系列以狀態變量的方式傳遞的參數,也被稱爲lambda表達式的捕獲列表。

6. lambda表達式的通用語法

[StateVar1,StateVar1](Type& param) { // }

若是要在lambda表達式中修改這些狀態變量,可添加關鍵字multable:

[StateVar1, StateVar1](Type& param)  multable { // }

這樣即可在lambda表達式中修改捕獲列表[]中指定的變量,但離開lambda表達式後,這些修改便無效,要確保在lambda表達式內部對狀態變量的修改在其外部也有效,應按照引用傳遞它們:

[&StateVar1, &StateVar1](Type& param) { // }

lambda表達式還能夠接受多個輸入參數,爲此可用逗號分隔:

[StateVar1, StateVar1](Type1& param1,Type2& param2) { // }

若是要向編譯器明確指明返回類型,可以使用->,以下:

[StateVar1, StateVar1](Type1& param1,Type2& param2)-> ReturnType { return (value or expression); }

最後,複合語句{}可包含多條用分號分割的語句:以下

[StateVar1, StateVar1](Type1& param1,Type2& param2)-> ReturnType { Statement 1; Statement 2;return (value or expression); }

若是lambda表達式包含多行代碼,必須顯示地指明返回類型。

7. 二元函數對應的lambda表達式

二元函數接受兩個參數,還可返回一個值,與之等價的lambda表達式以下:

[...] (Type1& param1,Type2& param2){ // }

8. 二元謂詞對應的lambda表達式

返回true或false,可幫助決策的二元函數被稱爲二元謂詞函數。這種謂詞可用於std::sort等排序算法。

[...] (Type1& param1,Type2& param2){ return bool expression; }

9. 總結

僅當lambda表達式簡潔、高效時才能使用它;

記住lambda表達式老是以[]打頭;

除非使用關鍵字multable進行指定,不然不能修改捕獲列表中指定的狀態變量;

lambda表達式是實現了operator()的匿名類或結構;

注意使用const對參數進行限定;

lambda表達式的語句塊包含多條語句時,要顯式的指定返回類型;

不要使用很長的包含多條語句的lambda表達式,而應轉而使用函數對象,由於每次使用lambda表達式時,都要從新定義它,這無助於提升代碼的重用性。

******************************************************************************************************

2015-8-3

相關文章
相關標籤/搜索