10.3定制操作
- 很多算法都會比較輸入序列中的元素。默認情況下,這類算法使用元素類型的<或==運算符完成比較。標準庫還為這些算法定義了額外的版本,允許我們提供自己定義的操作
- 來代替默認運算符。
- 例如,sort算法默認使用元素類型的<運算符。但可能我們希望的排序順序與<所定義的順序不同,或是我們的序列可能保存的是未定義<運算符的元素類型(如Sales_data)在這兩種情況下,都需要重載sort的默認行為。
10.3.1向算法傳遞函數
- 作為一個例子,假定希望在調用elimDups(參見10.2.3節,第343頁)后打印vector的內容。此外還假定希望單詞按其長度排序,大小相同的再按字典序排列。為了按長度重排vector,我們將使用sort的第二個版本,此版本是重載過的,它接受第三個參數,此參數是一個謂詞(predicate)。
謂詞
- 謂詞是一個可調用的表達式,其返回結果是一個能用作條件的值。標準庫算法所使用的謂詞分為兩類:一元謂詞(unarypredicate,意味著它們只接受單一參數)和二元謂詞(意味著它們有兩個參數)。接受謂詞參數的算法對輸入序列中的元素調用謂詞。因此,元素類型必須能轉換為謂詞的參數類型。
- 接受一個二元謂詞參數的sort版本用這個謂詞代替〈來比較元素。我們提供給sort的謂詞必須滿足將在11.2.2節(第378頁)中所介紹的條件。當前,我們只需知道,此操作必須在輸入序列中所有可能的元素值上定義一個一致的序。我們在6.2.2節(第189頁)中定義的isShorter就是一個滿足這些要求的函數,因此可以將isShorter傳遞給sorta這樣做會將元素按大小重新排序:/比較函數,用來按長度排序單詞
/ / 比較函數,用來按長度排序單詞
bool isShorter(const string &sl, const string &s2){return si.size () < s2.size ();
}
/ / 按長度由短至長排序words
sort(words.begin(), words.end(), isShorter);
- 如果words包含的數據與10.2.3節 (第 343頁)中~ 樣,此調用會將words重排,使得 所有長度為3的單詞排在長度為4 的單詞之前,然后是長度為5的單詞,依此類推
排序算法
- 在我們將words按大小重排的同時,還希望具有相同長度的元素按字典序排列。為 了保持相同長度的單詞按字典序排列,可以使用stable_sort算法。這種穩定排序算法維持相等元素的原有順序。
- 通常情況下,我們不關心有序序列中相等元素的相對順序,它們畢竟是相等的。但是,在本例中,我們定義的“相等”關系表示“具有相同長度”。而具有相同長度的元素,如果看其內容,其實還是各不相同的。通過調用stable_sort,可以保持等長元素間的字典序:
10.3.2 lambda 表達式
- 根據算法接受一元謂詞還是二元謂詞,我們傳遞給算法的謂詞必須嚴格接受一個或兩個參數。但是,有時我們希望進行的操作需要更多參數,超出了算法對謂詞的限制。例如,為上一節最后一個練習所編寫的程序中,就必須將大小5硬編碼到劃分序列的謂詞中。如果在編寫劃分序列的謂詞時,可以不必為每個可能的大小都編寫一個獨立的謂詞,顯然更有實際價值。
- 一個相關的例子是,我們將修改10.3.1節 (第 345頁)中的程序,求大于等于一個給定長度的單詞有多少。我們還會修改輸出,使程序只打印大于等于給定長度的單詞。
- 我們的新問題是在v ec to r中尋找第一個大于等于給定長度的元素。一旦找到了這個 元素,根據其位置,就可以計算出有多少元素的長度大于等于給定值。
- 我們可以使用標準庫fin d _ if算法來查找第一個具有特定大小的元素。類 似 find (參 見 10.1節,第 336頁),fin d _ if算法接受一對迭代器,表示一個范圍。但 與 find 不同的是,fin d _ if的第三個參數是一個謂詞。fin d _ if算法對輸入序列中的每個元素 調用給定的這個謂詞。它返回第一個使謂詞返回非0值的元素,如果不存在這樣的元素,則返回尾迭代器。
- 編寫一個函數,令其接受一個 string 和一個長度,并返回一個b o o l值表示該 s tr in g 的長度是否大于給定長度,是一件很容易的事情。但是,find_if接受一元謂詞,我們傳遞給find_if的任何函數都必須嚴格接受一個參數,以便能用來自輸入序列 的一個元素調用它。沒有任何辦法能傳遞給它第二個參數來表示長度。為了解決此問題,需要使用另外一些語言特性。
介紹lambda
- 我們可以向一個算法傳遞任何類別的可調用對象(callable object)<>對于一個對象或一個表達式,如果可以對其使用調用運算符(參見1.5.2節,第21頁),則稱它為可調用
的。即,如果e是一個可調用的表達式,則我們可以編寫代碼e(args),其中args是一個逗號分隔的一個或多個參數的列表。 - 到目前為止,我們使用過的僅有的兩種可調用對象是函數和函數指針(參見6.7節,第221頁)。還有其他兩種可調用對象:重載了函數調用運算符的類,我們將在14.8節(第506頁)介紹,以及lambda表達式(lambdaexpression)。
- 一個lambda表達式表示一個可調用的代碼單元。我們可以將其理解為一個未命名的內聯函數。與任何函數類似,一個lambda具有一個返回類型、一個參數列表和一個函數體。但與函數不同,lambda可能定義在函數內部。一個lambda表達式具有如下形式
- [capture list](parameter list)->return type{function body}其中,capturelist(捕獲列表)是一個lambda所在函數中定義的局部變量的列表(通常為空);return type、parameter list和function body與任何普通函數一樣,分別表示返回類型、參數列表和函數體。但是,與普通函數不同,lambda必須使用尾置返回(參見6.3.3節,第206頁)來指定返回類型。
- 我們可以忽略參數列表和返回類型,但必須永遠包含捕獲列表和函數體? ?auto f=[] {return 42;};
- 此例中,我們定義了一個可調用對象f , 它不接受參數,返回42。 lambda的調用方式與普通函數的調用方式相同,都是使用調用運算符:cout ? f () ? endl; // 打印 42
- 在 lambda中忽略括號和參數列表等價于指定一個空參數列表。在此例中,當調用f 時,參數列表是空的。如果忽略返回類型,lambda根據函數體中的代碼推斷出返回類型。 如果函數體只是一個return 語句,則返回類型從返回的表達式的類型推斷而來。否則, 返回類型為void
- 如果lambda的函數體包含任何單一 return 語句之外的內容,且未指定返回 類型,則返回void
向lambda傳遞參數
- 與一個普通函數調用類似,調用一個lambda時給定的實參被用來初始化lambda的形參。通常,實參和形參的類型必須匹配。但與普通函數不同,lambda不能有默認參數(參見 6.5.1節,第 211頁)。因此,一個lambda調用的實參數目永遠與形參數目相等。一旦形參初始化完畢,就可以執行函數體了。
- 作為一個帶參數的lambda的例子,我們可以編寫一個與isShorter函數完成相同功能的lambda:
- [] (const string &a ,const string &b){ return a.size()? <? b.size(); }
- 空捕獲列表表明此lambda不使用它所在函數中的任何局部變量。lambda的參數與isShorter的參數類似,是const string的引用。lambda的函數體也與isShorter類似,比較其兩個參數的size(),并根據兩者的相對大小返回一個布爾值
使用捕獲列表
- 我們現在巳經準備好解決原來的問題了—— 編寫一個可以傳遞給find _ if的可調用 表達式。我們希望這個表達式能將輸入序列中每個string 的長度與biggies函數中的 sz參數的值進行比較。 雖然一個lambda可以出現在一個函數中,使用其局部變量,但它只能使用那些明確指明的變量。一個lambda通過將局部變量包含在其捕獲列表中來指出將會使用這些變量。 捕獲列表指引lambda在其內部包含訪問局部變量所需的信息。
- 在本例中,我們的lambda會捕獲sz , 并只有單一的string參數。其函數體會將string的大小與捕獲的sz的值進行比較:
- [sz](const string &a) { return a.size () >= sz; };
- lambda以一對[]開始,我們可以在其中提供一個以逗號分隔的名字列表,這些名字都是它所在函數中定義的。
- 由于此lambda捕 獲 sz , 因此lambda的函數體可以使用sz 。lambda不捕獲words,因此不能訪問此變量。如果我們給lambda提供一個空捕獲列表,則代碼會編譯錯誤:
- [](const string &a) ( return a.size() >= sz; };? ? ? ? ?/ / 錯誤:SZ未捕獲? ?只有前面的[]里面列舉出來,后面的{} 里面才可以使用
- 一 個 lambda只有在其捕獲列表中捕獲一個它所在函數中的局部變量,才能在函數體中使用該變量
調 用find_if
- 使用此lambda,我們就可以查找第一個長度大于等于s z 的元素:
- auto wc = find_if(words.begin(), words.end(), [sz] (const string &a) {return a.size() >= sz; });??/ / 獲取一個迭代器,指向第一個滿足size()>= sz的元素
- 這里對find _ if的調用返回一個迭代器,指向第一個長度不小于給定參數sz的元素。 如果這樣的元素不存在,則返回words.end()的一個拷貝。 我們可以使用fin d _ if返回的迭代器來計算從它開始到words的末尾一共有多少個元素 (參 見 3.4.2節,第 99頁):
- / / 計算滿足size >= sz的元素的數目
- auto count = words.end() - wc; cout ? count << " " << make_plural(count, "word", "s") ? " of length " << sz ? '* or longer" ? endl;我們的輸出語句調用m ake_plural(參見6.3.2節,第201頁)來輸出"w ord”或"w ords",具體輸出哪個取決于大小是否等于1。
for_each 算法
- 問題的最后一部分是打印words中長度大于等于sz 的元素。為了達到這一目的,我們可以使用for_each算法。此算法接受一個可調用對象,并對輸入序列中每個元素調用此對象:
- / / 打卬長度大于等于給定值的單詞,每個單詞后面接一個空格
- for_each(wc, words.end(), [](const string &s)(cout ? s ? " ";}); cout ? endl;
- 此 lambda中的捕獲列表為空,但其函數體中還是使用了兩個名字:s 和 c o u t,前者是它自己的參數。
- 捕獲列表為空,是因為我們只對lambda所在函數中定義的(非s ta tic ) 變量使用捕獲列表。一個lambda可以直接使用定義在當前函數之外的名字。在本例中,cout不是定義在biggies中的局部名字,而是定義在頭文件iostream 中。因此,只要在b iggies 出現的作用域中包含了頭文件io s tre a m ,我們的lambda就可以使用cout。
- 列表只用于局部非static變量,lambda可以直接使用局部static變量和在它所在函數之外聲明的名字
#include <iostream>
#include <cstdio>#include <memory>
#include <vector>
#include <algorithm>void elimDups(std::vector<std::string> &words){//按字典順序排序sort(words.begin(),words.end());//將words進行字典排序,刪除重復的單詞auto end_unique = unique(words.begin(),words.end());//end_unique指向不重復元素區間的后一位//https://www.cplusplus.com/reference/algorithm/unique///https://www.jianshu.com/p/b8987c8d80b8words.erase(end_unique,words.end());//將多余重復的元素刪除
}std::string make_plural(size_t ctr, const std::string &word, const std::string &ending = "s")
{return (ctr > 1) ? word + ending : word;
}void biggies(std::vector<std::string>&words,std::vector<std::string>::size_type sz){elimDups(words);//將words按照字典排序并且刪除重復的單詞//按照長度排序 長度相同的單詞維持字典序std::stable_sort(words.begin(),words.end(),[](const std::string &a,const std::string &b){ return a.size() < b.size(); });//獲取一個迭代器 指向第一個滿足size() >= sz 的元素auto wc = std::find_if(words.begin(),words.end(),[sz](const std::string &a){ return a.size() > sz; });//計算滿足size >= sz 的元素的數目auto count = words.end() - wc;std::cout << count << " " << make_plural(count, "word", "s")<< " of length " << sz << " or longer" <<std::endl;//打印長度大于等于給定數值的單詞 每個單詞的后面接入一個空格std::for_each(wc,words.end(),[](const std::string &s){std::cout << s << " ";});std::cout<<" "<< std::endl;}using namespace std;
int main(){std::vector<std::string>words {"Hi","Hello","one","two","Hi","three","one"};std::vector<std::string>::size_type sz = 2;biggies(words,sz);
}
10.3.3 lambda捕獲和返回
- 當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)類類型。 我們將在14.8.1節 (第 507頁)介紹這種類是如何生成的。目前,可以這樣理解,當向一個函數傳遞一個lambda時,同時定義了一個新類型和該類型的一個對象:傳遞的參數就是此編譯器生成的類類型的未命名對象。類似的,當使用a u to 定義一個用lambda初始 化的變量時,定義了一個從lambda生成的類型的對象。 默認情況下,從lambda生成的類都包含一個對應該lambda所捕獲的變量的數據成員。 類似任何普通類的數據成員,lambda的數據成員也在lambda對象創建時被初始化
值捕獲
- 類似參數傳遞,變量的捕獲方式也可以是值或引用。表 10.1 (第352頁)列出了幾種不同的構造捕獲列表的方式。到目前為止,我們的lambda采用值捕獲的方式。與傳值參 數類似,采用值捕獲的前提是變量可以拷貝。與參數不同,被捕獲的變量的值是在lambda 創建時拷貝,而不是調用時拷貝:
- 由于被捕獲變量的值是在lambda創建時拷貝,因此隨后對其修改不會影響到lambda內對應的值
引用捕獲
- 我們定義lambda時可以采用引用方式捕獲變量。例如
- V l之前的&指出v l應該以引用方式捕獲。一個以引用方式捕獲的變量與其他任何類型的引用的行為類似。當我們在lambda函數體內使用此變量時,實際上使用的是引用所綁定的對象。在本例中,當lambda返回v l 時,它返回的是v l 指向的對象的值。 引用捕獲與返回引用(參見6.3.2節,第 201頁)有著相同的問題和限制。如果我們采用引用方式捕獲一個變量,就必須確保被引用的對象在lambda執行的時候是存在的。 lambda捕獲的都是局部變量,這些變量在函數結束后就不復存在了。如果lambda可能在函數結束后執行,捕獲的引用指向的局部變量已經消失。
- 引用捕獲有時是必要的。例如,我們可能希望biggies 函數接受一個ostream的引用,用來輸出數據,并接受一個字符作為分隔符:
void biggies1(std::vector<std::string>&words,std::vector<std::string>::size_type sz,std::ostream &os = std::cout,char c = '\n'){//和之前一樣的重排words的代碼//打印count的語句改為打印到osstd::for_each(words.begin(),words.end(),[&os,c](const std::string &s){os << s << c;});
}using namespace std;
int main(){std::vector<std::string>words {"Hi","Hello","one","two","Hi","three","one"};std::vector<std::string>::size_type sz = 2;biggies1(words,sz);
}
- 我們不能拷貝ostream 對象 (參 見 8.1.1節,第 279頁),因此捕獲os 的唯一方法就是捕獲其引用(或指向os 的指針)。 當我們向一個函數傳遞一個lambda時,就像本例中調用for_each那樣 lambda 會立即執行。在此情況下,以引用方式捕獲os 沒有問題,因為當for_each執行時, biggies 中的變量是存在的。 我們也可以從一個函數返回lambd.函數可以直接返回一個可調用對象,或者返回一 個類對象,該類含有可調用對象的數據成員。如果函數返回一個lambda,則與函數不能返回一個局部變量的引用類似,此 lambda也不能包含引用捕獲。
- 當以引用方式捕獲一個變量時,必須保證在lambda執行時變量是存在的
建議: 盡量保持]ambda 的變量捕獲簡單化
- 一個lambda捕獲從lambda被創建(即,定義lambda的代碼執行時) 到 lambda自身執行(可能有多次執行)這段時間內保存的相關信息。確保lambda每次執行的時候 這些信息都有預期的意義,是程序員的責任。
- 捕獲一個普通變量,如 int、string 或其他非指針類型,通常可以采用簡單的值捕獲方式。在此情況下,只需關注變量在捕獲時是否有我們所需的值就可以了。
- 如果我們捕獲一個指針或迭代器,或采用引用捕獲方式,就必須確保在lambda執行時,綁定到迭代器、指針或引用的對象仍然存在。而且,需要保證對象具有預期的值,
在 lambda從創建到它執行的這段時間內,可能有代碼改變綁定的對象的值。也就是說, 在指針(或引用)被捕獲的時刻,綁定的對象的值是我們所期望的,但在 lambda執行時,該對象的值可能已經完全不同了。 - 一般來說,我們應該盡量減少捕獲的數據量,來避免潛在的捕獲導致的問題。而且,如果可能的話,應該避免捕獲指針或引用。
隱式捕獲
- 除了顯式列出我們希望使用的來自所在函數的變量之外,還可以讓編譯器根據lambda 體中的代碼來推斷我們要使用哪些變量。為了指示編譯器推斷捕獲列表,應在捕獲列表中寫一個&或=。&告訴編譯器采用捕獲引用方式,=則表示采用值捕獲方式。例如,我們可以重寫傳遞給find _ if的lambda:
- 當我們混合使用隱式捕獲和顯式捕獲時,捕獲列表中的第一個元素必須是一個&或=。此符號指定了默認捕獲方式為引用或值。當混合使用隱式捕獲和顯式捕獲時,顯式捕獲的變量必須使用與隱式捕獲不同的方式。即,如果隱式捕獲是引用方式(使用了&),則顯式捕獲命名變量必須采用值方式,因此不能在其名字前使用&。類似的,如果隱式捕獲采用的是值方式(使用了=),則顯式捕獲命名變量必須采用引用方式,即,在名字前使用&。
指定 lambda返回類型
- 到目前為止,我們所編寫的lambda都只包含單一的return 語句。因此,我們還未遇到必須指定返回類型的情況。默認情況下,如果一個lambda體包含return之外的任何語句,則編譯器假定此lambda返回void。與其他返回void 的函數類似,被推斷返回void 的lambda不能返回值。
- 下面給出了一個簡單的例子,我們可以使用標準庫transform 算法和一個 lambda 來將一個序列中的每個負數替換為其絕對值:
- 函數transform 接受三個迭代器和一個可調用對象。前兩個迭代器表示輸入序列,第三 個迭代器表示目的位置。算法對輸入序列中每個元素調用可調用對象,并將結果寫到目的位置。如本例所示,目的位置迭代器與表示輸入序列開始位置的迭代器可以是相同的。當輸入迭代器和目的迭代器相同時,transform 將輸入序列中每個元素替換為可調用對象操作該元素得到的結果。
- 在本例中,我們傳遞給transform 一個 lambda,它返回其參數的絕對值。lambda 體是單一的return語句,返回一個條件表達式的結果。我們無須指定返回類型,因為可以根據條件運算符的類型推斷出來。
- 但是,如果我們將程序改寫為看起來是等價的if 語句,就會產生編譯錯誤:返回的類型和編譯推導出來的類型不一致
10.3.4參數綁定
- 對于那種只在一兩個地方使用的簡單操作,lambda表達式是最有用的。如果我們需要 在很多地方使用相同的操作,通常應該定義一個函數,而不是多次編寫相同的lambda表達式。類似的,如果一個操作需要很多語句才能完成,通常使用函數更好。
- 如果 lambda的捕獲列表為空,通常可以用函數來代替它。如前面章節所示,既可以用一個lambda,也可以用函數isShorter來實現將vector中的單詞按長度排序。類似的,對于打印vector內容的lambda,編寫一個函數來替換它也是很容易的事情,這個函數只需接受一個string并在標準輸出上打印它即可。 但是,對于捕獲局部變量的lambda,用函數來替換它就不是那么容易了。例如,我們用在find _ if調用中的lambda比較一個string和一個給定大小。我們可以很容易地編寫一個完成同樣工作的函數:
- 但是,我們不能用這個函數作為find_if的一個參數。如前文所示, 接受一個 —元謂詞,因此傳遞給find_if的可調用對象必須接受單一參數。biggies傳遞給find_if的 lambda使用捕獲列表來保存sz。為了用check_size來代替此lambda,必須解決如何向sz 形參傳遞一個參數的問題。
標準庫bind函數
- 我們可以解決向check_size傳遞一個長度參數的問題,方法是使用一個新的名為bind的標準庫函數,它定義在頭文件functional中。可以將bind函數看作一個通用的函數適配器(參見9.6節,第 329頁),它接受一個可調用對象,生成一個新的可調用對象來"適應”原對象的參數列表。
- ?調用bind的一般形式為: auto newCallable = bind (callable, arg_list);
- 其中,newCallable本身是一個可調用對象,arg list是一個逗號分隔的參數列表,對應給定的callable的參數。即,當我們調用newCallable時,newCallable會調用callable,并傳 遞給它arg list中的參數。
- argjist中的參數可能包含形如_n的名字,其中n是一個整數。這些參數是“占位符”,表示newCallable的參數,它們占據了傳遞給newCallable的參數的"位置”。數值n表示生成的可調用對象中參數的位置:_1 newCallable的第一個參數,_2為第二個參數,依此類推。
綁定check_size的 sz參數
- 作為一個簡單的例子,我們將使用bind生成一個調用check_size的對象,如下 所示,它用一個定值作為其大小參數來調用check_size:
- // check6是一個可調用對象,接受一個string類型的參數,并用此string和值6來調用check_size
- auto check6 = bind(check_size, _1, 6);
- 此 bind調用只有一個占位符,表示check6只接受單一參數。占位符出現在arg list的 第一個位置,表 示 check6的此參數對應check_size的第一個參數。此參數是一個 const string&o因此,調用check6必須傳遞給它一*個 string類型的參數,check6 會將此參數傳遞給check_sizeo
- string s = "hello"; bool bl = check6 (s) ; // check6 (s)會調用 check_size (s, 6)
- 使用bind,我們可以將原來基于lambda的 f ind_if調用:
- auto wc = find_if(words.begin(), words.end(), [sz] (const string &a)
- 替換為如下使用check_size的版本:
- auto wc = find_if(words.begin(), words.end()r bind(check_size, _1, sz));
- 此 bind調用生成一個可調用對象,將 check_size的第二個參數綁定到sz 的值。當 find_if對 words中的string調用這個對象時,這些對象會調用check_size,將給定的string和 sz 傳遞給它。因此, 可以有效地對輸入序列中每個string 調用check_size,實現string的大小與sz 的比較。
使用 placeholders名字
- 名字_n都定義在一個名為placeholders的命名空間中,而這個命名空間本身定義在 std命名空間(參見3.1節,第 74頁 )中 。為了使用這些名字,兩個命名空間都要寫 上。與我們的其他例子類似,對 bind的調用代碼假定之前己經恰當地使用了 using聲明。例如, 對應的using聲明為:
- using std::placeholders::_1;
- 此聲明說明我們要使用的名字_1 定義在命名空間placeholders中,而此命名空間又定義在命名空間std中。 對每個占位符名字,我們都必須提供一個單獨的using聲明。編寫這樣的聲明很煩人,也很容易出錯。可以使用另外一種不同形式的using語句(詳細內容將在18.2.2節 (第702頁)中介紹),而不是分別聲明每個占位符,如下所示:
- using namespace namespace_name;? ?例如 using namespace std;
- 這種形式說明希望所有來自namespace_name 的名字都可以在我們的程序中直接使用。例 如:
- using namespace std::placeholders;
- 使得由placeholders定義的所有名字都可用。與 bind函數一樣,placeholders命名空間也定義在functional頭文件中。
bind的參數
- 如前文所述,我們可以用bind修正參數的值。更一般的,可以用bind綁定給定可調用對象中的參數或重新安排其順序。例如,假定f 是一個可調用對象,它有5 個參數, 則下面對bind的調用:
- auto g = bind(f, a, b, _2, c, _1);? ?// g 是一個有兩個參數的可調用對象
- 生成一個新的可調用對象,它有兩個參數,分別用占位符_2和_1表示。這個新的可調用對象將它自己的參數作為第三個和第五個參數傳遞給f。f 的第一個、第二個和第四個參數分別被綁定到給定的值a、b 和 c。傳遞給g 的參數按位置綁定到占位符。即,第一個參數綁定到一1,第二個參數綁定到 _2。因此,當我們調用g 時,其第一個參數將被傳遞給f 作為最后一個參數,第二個參 數將被傳遞給f 作為第三個參數。實際上,這個bind調用會將g(_l, _2)映射為f (a, b, _2, c, _1)。即,對 g 的調用會調用f,用 g 的參數代替占位符,再加上綁定的參數a、b 和 c。例如, 調用g(x,Y)會調用f (a, b, Y, c, X)
用 bind重排參數順序
- 下面是用bind重排參數順序的一個具體例子,我們可以用bind顛倒 isShroter 的含義:
- sort(words.begin(), words.end(), isShorter);??/ / 按單詞長度由短至長排序
- sort(words.begin(), words.end(), bind(isShorter, _2, _1));/ / 按單詞長度由長至短排序
- 在第一個調用中,當 sort需要比較兩個元素A 和 B 時,它會調用isShorter (A, B) ,在第二個對sort的調用中,傳遞給isShorter的參數被交換過來了。因此,當 sort 比較兩個元素時,就好像調用isShorter (B,A)-樣。
綁定引用參數
- 默認情況下,bind的那些不是占位符的參數被拷貝到bind返回的可調用對象中。 但是,與 lambda類似,有時對有些綁定的參數我們希望以引用方式傳遞,或是要綁定參數的類型無法拷貝。例如,為了替換一個引用方式捕獲ostream的 lambda:
- for_each(words.begin(), words.end(),?[&os, c] (const string &s) ( os << s ? c; });? ? ?// os是一個局部變量,引用一個輸出流 ,c 是一個局部變量,類型為char
?