參考《21天學通C++》第21與第22章節,對函數對象進行介紹,同時經過lambda表達式這一匿名函數對象的簡潔方式加深對函數對象的理解。本篇博文的主要內容是:css
(1) 函數對象的概念;html
(2) 將函數對象用做謂詞;算法
(3) 如何使用函數對象實現一元、二元謂詞;express
(4) 如何編寫lambda表達式;函數
(5) 如何將lambda表達式用做謂詞;spa
(6) 如何編寫可存儲和可操做狀態的lambda表達式。指針
函數對象是C++實體,從概念上講,函數對象是用做函數的對象;從實現上說,函數對象是實現了operator()的類的對象。雖然函數和函數指針也可視爲函數對象,可是吸納了operator()的類的對象才能保存狀態,才能用於標準模板庫算法。C++經常使用於STL算法的函數對象可分爲下面兩種類型:(1) 一元函數:接受一個參數的函數,若是一元函數返回一個布爾值,稱之爲謂詞;(2) 二元函數:接受兩個參數的函數,若返回bool,則稱爲二元謂詞函數。返回bool值得函數對象經常用來進行判斷的算法,組合兩個函數對象的函數對象稱之爲自適應函數對象。code
對於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 << ' ';});
若是可以使用結構的對象來存儲信息,則使用在結構中實現的函數對象的優勢將顯現出來。
返回布爾值的一元函數就是謂詞,這種函數可用於STL算法的判斷。
一元謂詞被大量用於STL算法中,例如,std::partition算法使用一元謂詞來劃分範圍,stable_partition算法也使用一元謂詞劃分範圍,但保持元素的相對順序不變。諸如std::find_if等查找函數以及std::remove_if等刪除元素的函數也使用一元謂詞,其中std::remove_if刪除指定範圍內知足謂詞條件的元素。
若是函數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中。
接受兩個參數並返回一個布爾值的函數是二元謂詞,這種函數用於諸如std::sort等STL函數中。使用排序二元謂詞實現排序,經過實現operator()來動態排序。
不少STL算法都使用二元謂詞,好比刪除相鄰重複元素的std::unique()、排序算法sort、排序並保持相對順序的std::stable_sort以及兩個範圍進行操做的std::transform。
在結構或類中實現函數對象時,它將比簡單函數有用的多,由於它也可用於存儲與狀態相關的信息。謂詞是一類特殊的函數對象。
可將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 << ' ';
}
};
以方括號[]打頭,告訴編譯器,接下來是一個lambda表達式,方括號後面是一個參數列表,該參數列表與不使用lambda表達式時提供給operator()參數列表相同。
與一元operator(Type)對應的lambda表達式接收一個參數,定義以下:
[](Type paramName){ // lambda expression code here}也可用引用來傳參:[](Type& paramName){ // lambda expression code here}。
謂詞可幫助作出決策,一元謂詞是返回bool類型的一元表達式。舉例以下:
[](int& Num){return ((Num % 2) == 0);}
在這裏,返回值的性質是爲了讓編譯器知道該lambda表達式的返回類型爲bool。在算法中可將lambda表達式用於一元謂詞,如可在find_if中使用上述lambda表達式找出集合中的偶數。若是謂詞返回true,find_if將返回一個指向相應元素的迭代器,指出找到一個知足條件的元素。
對於上面的一元謂詞實如今整數能被2整除時返回true,若是讓它更通用,在數字能被用戶指定除數整除時返回true,可採用狀態變量:
int Divisor = 2;
[Divisor ](int& Num){return ((Num % Divisor ) == 0);}
一系列以狀態變量的方式傳遞的參數,也被稱爲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表達式包含多行代碼,必須顯示地指明返回類型。
二元函數接受兩個參數,還可返回一個值,與之等價的lambda表達式以下:
[...] (Type1& param1,Type2& param2){ // }
返回true或false,可幫助決策的二元函數被稱爲二元謂詞函數。這種謂詞可用於std::sort等排序算法。
[...] (Type1& param1,Type2& param2){ return bool expression; }
僅當lambda表達式簡潔、高效時才能使用它;
記住lambda表達式老是以[]打頭;
除非使用關鍵字multable進行指定,不然不能修改捕獲列表中指定的狀態變量;
lambda表達式是實現了operator()的匿名類或結構;
注意使用const對參數進行限定;
lambda表達式的語句塊包含多條語句時,要顯式的指定返回類型;
不要使用很長的包含多條語句的lambda表達式,而應轉而使用函數對象,由於每次使用lambda表達式時,都要從新定義它,這無助於提升代碼的重用性。
******************************************************************************************************
2015-8-3