C++primer第十章 泛型算法 10.3 定制操作

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

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446699.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446699.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446699.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL 查詢語句

文章目錄1、簡單查詢2、去除單列的重復結果查詢3、去除多列的重復結果查詢4、限制查詢結果條數5、對查詢結果排序&#xff08;1&#xff09;按照單個列的值進行排序&#xff08;2&#xff09;按照多個列的值進行排序6、帶搜索條件查詢&#xff08;1&#xff09;簡單搜索條件查詢…

2000年考研英語閱讀理解文章一

文章詳細講解網址 注意點 1.文章開篇第一句話往往是文章所想要通過后面講解的事情表達出來的最終觀點 2.當詢問到作者觀點時,往往在最后一段,一般以下形式呈現: 1)few people …(這就是作者的觀點) 2)I think 后面舉什么別人所說的話,如果不是表達了贊同,則都是別人的觀點,而…

C++primer第十章 泛型算法 10.4 再探迭代器 10.5 泛型算法結構

除了為每個容器定義的迭代器之外&#xff0c;標準庫在頭文件iterator中還定義了額外幾種迭代器。這些迭代器包括以下幾種。插入迭代器(insert iterator)&#xff1a;這些迭代器被綁定到一個容器上&#xff0c;可用來向容器插入元素。流迭代器(stream iterator)&#xff1a;這些…

codeforces 546A-C語言解題報告

546A題目網址 題目解析 1.輸入 k(成本),n(擁有的錢),w(要買的個數),輸出還需要向朋友借多少錢? 舉例: 輸入: 3 17 4 輸出: 13 2.注意: 1)第i個,需要i*k個價錢,所以需要使用for循環運算花費 2)當擁有的錢足夠買時,不需要借錢,輸出為0 代碼 #include<stdio.h> #inclu…

java.sql.SQLException: ORA-00604: 遞歸 SQL 級別 1 出現錯誤

文章目錄1、報錯信息2、原因分析3、解決方案1、報錯信息 java.sql.SQLException: ORA-00604: 遞歸 SQL 級別 1 出現錯誤 ORA-01000: 超出打開游標的最大數 ORA-00604: 遞歸 SQL 級別 1 出現錯誤 ORA-01000: 超出打開游標的最大數 ORA-01000: 超出打開游標的最大數at oracle.jd…

C++primer第十一章 關聯容器 11.1使用關聯容器 11.2 關聯容器概述

關聯容器和順序容器有著根本的不同&#xff1a;關聯容器中的元素是按關鍵字來保存和訪問的。與之相對&#xff0c;順序容器中的元素是按它們在容器中的位置來順序保存和訪問的。雖然關聯容器的很多行為與順序容器相同&#xff0c;但其不同之處反映了關鍵字的作用關聯容器支持高…

codeforces 791A-C語言解題報告

791A題目網址 題目解析 1.輸入a,b,每一年a3;b2,問多少年a>b? 2.因為不知道需要循環多少次,使用while循環 代碼 #include<stdio.h> #include<stdlib.h> #include<string.h> int main() {int a,b,i0;scanf("%d %d",&a,&b);while(a&l…

Redis Mac下安裝與使用

目錄一、下載安裝包二、編譯三、服務端與客戶端命令1、服務端啟動命令2、客戶端連接命令3、服務端關閉命令一、下載安裝包 官網地址&#xff1a;http://redis.io/download 下載后&#xff0c;解壓放到任意目錄下。 二、編譯 打開終端&#xff0c;切換到 Redis 根目錄&#x…

C++primer第十一章 關聯容器 11.3關聯容器操作 11.4 無序容器

11.3關聯容器操作 除了表9.2(第295頁)中列出的類型&#xff0c;關聯容器還定義了表11.3中列出的類型。這些類型表示容器關鍵字和值的類型。對于set類型&#xff0c;key_type和value type是一樣的&#xff1b;set中保存的值就是關鍵字。在一個map中&#xff0c;元素是關鍵字_值…

codeforces 977A-C語言解題報告

977A題目網址 題目解析 1,輸入數字n,運算次數k,當n最后一個數字是0時,n/10;當n最后一個數字不是0時,n-1;輸出n 舉例: 輸入: 512 4 輸出: 50 2.注意:當n最后一個數字是0時,使用n%100去判斷 代碼 #include<stdio.h> #include<stdlib.h> #include<string.h>…

SpringBoot 整合Dubbo

文章目錄一、工程目錄結構二、創建工程項目1、創建接口工程&#xff08;cw-dubbo-api&#xff09;&#xff08;1&#xff09;pom.xml&#xff08;2&#xff09;創建接口類&#xff08;LoginService&#xff09;2、創建服務提供者工程&#xff08;cw-dubbo-provider&#xff09;…

macos實現輸入文件輸入結束符

在clion軟件中&#xff0c;執行cin>>value ,如何手動輸入結束符號&#xff1f;&#xff1f;需要在debug環境下&#xff0c;然后&#xff0c;使用command D 實現此功能

2000年考研英語閱讀理解文章二

文章詳細解析 注意點 1.文章標題選擇,查看文章中一直在重復提及的話語: 如:我們沒有進化了—>標題:人類進化無路可走 知識點 ----單詞 1.offspring n孩子,后代 2.Utopia n烏托邦,空想的完美境界 3.wholly adv完全地 4.comprehension n理解力 5.descendant n后代 6.mate …

Kafka Mac下安裝與使用

文章目錄一、下載安裝二、啟動Zookeeper三、啟動Kafka四、創建Topic五、查看Topic六、刪除Topic七、生產/消費數據八、查看消費組九、查看消費組詳情一、下載安裝 到 Kafka 官網下載&#xff1a;https://kafka.apache.org/downloads 下載好 tar包 后&#xff0c;執行下面命令…

C++primer第一章 開始

運算符打印endl,這是一個被稱為操縱符(manipulator)的特殊值。寫入endl 的效果是結束當前行&#xff0c;并將與設備關聯的緩沖區(buffer)中的內容刷到設備中。緩沖刷新操作可以保證到目前為止程序所產生的所有輸出都真正寫入輸出流中&#xff0c;而不是僅停留在內存中等待寫入流…

codeforces 617A-C語言解題報告

617A題目網址 題目解析 1.輸入x,能夠通過1,2,3,4,5去到達x,求最小到達x的步數. 舉例: 輸入: 12 輸出: 3 2.注意點: 要最小的步數,所以直接使用最大的5去比較判斷 1)當x<5時,只需要1 2)當x>5時,如果x%50(x能整除5),只需要x/5步數,不能整除則需要x/51步數 代碼 #inclu…

SpringBoot —— Bean的注入方式

文章目錄1、組件注解2、Component Bean3、Import(PlaceHolderClass)快速導入一個組件4、使用Spring提供的FactoryBean注入1、組件注解 注解描述Component組件定義不清晰時候的注解Controller控制器層Service服務層Repository數據層 注&#xff1a;添加注解的類需要與啟動類在…

如何保養電池

1&#xff0c;不要在低于0度和高于35度的范圍下使用電池&#xff0c;尤其是高溫環境下對電腦充電&#xff0c;對電池的破壞是不可逆轉的。2&#xff0c;放電過于徹底或者充電過于飽和&#xff0c;也會對電池的容量造成損耗。BMS 調整電池的充放電3&#xff0c;電腦長期不用&…

codeforces 116A-C語言解題報告

116A題目網址 題目解析 1.輸入n(n個循環),每一個循環-a,b;第一個循環只有b;最后一個循環只有-a;求其中在車上的最大人數? 舉例: 輸入: 4 0 3 2 5 4 2 4 0 輸出: 6 2.注意點:因為使用count計數時,count一直在改變,所以再加入一個max變量去記錄count中出現的最大數. 代碼 #…

SpringBoot —— @ComponentScan注解

文章目錄一、作用二、注解屬性說明三、使用方式一、作用 主要是從定義的掃描路徑中&#xff0c;找出標識了需要裝配的類自動裝配到Spring的bean容器中。 簡單的說就是 ComponentScan告訴Spring從哪里找到bean&#xff0c;一旦指定了&#xff0c;Spring就會將指定的包及其下級…