C Primer之泛型演算法lambda筆記

2021-07-11 19:51:09 字數 4199 閱讀 2442

泛型演算法中有部分演算法除了第乙個和第二個引數接受迭代器型別來表示範圍外,它的第三個引數型別是乙個謂詞

謂詞是乙個可呼叫表示式,分為一元謂詞(只接受乙個引數),二元謂詞(接受兩個引數)。

比如說常用的sort演算法,它的第三個引數就是乙個二元謂詞。

#include 

#include

#include

#include

using

namespace

std;

//定義乙個二元謂詞使sort演算法按字典的逆序排序

bool redict(const

string &s1, const

string &s2)

int main(void);

sort(container.begin(), container.end(), redict);

for(auto i = container.begin();i != container.end();i++)

//d c b a

return

0;}

以及partition演算法,它接受一元謂詞引數,並將呼叫一元謂詞返回true的值排在容器的前面,最終返回乙個排序後最後乙個返回true的值的之後的迭代器。

#include 

#include

#include

#include

using

namespace

std;

//找到vector中大於5的所有字串

bool size_bigger_than_5(const

string &s1)

else

}int main(void);

auto end = partition(container.begin(), container.end(), size_bigger_than_5);

for(auto i = container.begin();i != end;i++)

//最後輸出"abcdef", "abcde"

return

0;}

因此,謂詞限制了傳入引數的數量,但有時候我們又希望傳入更多的引數,比如說,我們使用partition演算法刷選出大於或等於string長度為n的字串,這個n由輸入流讀取,此時partition的一元謂詞必須讀入兩個引數,明顯不符合一元謂詞的定義,所以這時就需要使用lambda函式,多餘的引數通過捕獲列表傳輸。

int main(void);

int size;

cin >> size;

auto end = partition(container.begin(), container.end(),[size](const

string &s1));

for(auto i = container.begin();i != end;i++)

return

0;}

lambda表示式主要分為四個部分:捕獲列表,引數列表,返回型別,函式體。

其中,引數列表和返回型別都可以省略,捕獲列表和函式體必須包含。

在省略返回型別的時候,lambda表示式會根據函式體的return語句推斷返回型別,而如果沒有return語句的時候就會返回void。

在c++ primer上的p347的note的翻譯就個人的理解來說比較容易產生歧義:

note:如果lambda的函式體包含任何單一return語句之外的內容,且未指定返回型別,則返回void。

這裡並非指在沒有指定返回型別的時候如果函式體出現了return語句之外的語句就會返回void,比如以下情況不會返回void:

auto print = ;

std::cout

<< print() << std::endl;

//hello world

而是指如果lambda函式沒有出現return語句又沒有指定返回型別的時候,lambda就會返回void。

c++ primer中還舉到了乙個關於乙個lambda中多個return語句但沒有指定返回型別的例子(當然如果指定返回型別後多條return語句是沒有問題的):

transform(vi.begin(), vi.end(), vi.begin(), (int i));

//在以前老版本的編譯器會報錯

然而現在lambda表示式多條return語句已經支援,只要return的是同一型別。參考傳送門

lambda不支援預設引數。

捕獲列表中的變數是要求在在當前函式內的區域性變數,如果是全域性變數則不需要再通過捕獲列表來傳遞(因此std命名空間內的cout也不需要通過捕獲列表來傳遞)。

#include 

int global = 88;

int main(void);

//global變數是全域性變數,因此不需要傳遞

//first變數是當前的main函式內的區域性變數,因此需要捕獲列表

//second變數則作為lambda的引數傳進去

std::cout

<< sum(second) << std::endl;

return

0;}

lambda表示式實質生成了乙個未命名的類型別及其物件,並將物件返回,捕獲列表中的變數則作為物件的普通資料成員。

捕獲列表的捕獲同樣分為值捕獲和引用捕獲。

值捕獲值捕獲實質上就是將值賦值給物件的資料成員,這是在物件建立的時候進行的,而並非呼叫的時候進行,即使外界的值變了,物件的資料成員不變。

如果是值捕獲(預設賦值後不會改變物件的資料成員)而我們又想改變物件的資料成員的值,我們可以使用關鍵字mutable。

#include 

int main(void);

change = 0;

std::cout

<< fun();//12

return

0;}

引用捕獲

如果呼叫函式返回乙個lambda表示式並引用捕獲乙個變數,一定要確認該變數的生命週期,比如說該變數是乙個函式內的區域性變數,那麼就會隨著函式呼叫結束被銷毀,此時訪問lambda表示式物件的資料成員(即引用捕獲的值)就會發生不可預料的事情。

隱式捕獲

[=]將當前函式的所有區域性變數值捕獲到lambda中。

[&]將當前函式的所有區域性變數引用捕獲到lambda 中。

兩者可以混搭。

由於謂詞的限制,普通函式不能傳入過多的引數,因此上面我們通過lambda表示式的捕獲列表來傳遞過多的引數,除此之外,我們還能通過bind函式繫結來實現傳遞引數。

auto newcallable = bind(callable, arg_list)

其中bind函式定義在functional標頭檔案中,我們現在通過bind函式實現尋找字串陣列中長度大於等於n的字串

#include 

#include

#include

#include

#include

using

namespace

std;

using

namespace

std::placeholders;

bool size_bigger_than_n(const

string &s1, int n)

int main(void);

int n;

cin >> n;

for(auto i = container.begin();i != end;i++)

return

0;}

我們可以通過呼叫using宣告來使用佔位符:

using

namespace

std::placeholders::_1;

//一勞永逸的方法

using

namespace

std::placeholders;

bind函式對不是佔位符的引數缺省會使用值傳遞,即對傳遞的引數進行拷貝,但有時候我們會遇到不可拷貝的物件,此時則需要引用傳遞。

使用函式ref即可返回乙個可拷貝的引用物件。

c primer之10 2 初識泛型演算法

標準庫提供了超過100個演算法,這些演算法都有一致的結構和類似的用法。除了少數例外,標準庫演算法都對乙個範圍內的元素進行操作。範圍分別用第乙個元素和尾元素之後位置的迭代器。理解演算法的最基本的方法就是了解他們是否讀取元素 改變元素或重排元素。10.2.1 唯讀演算法 有find和accumulate...

C Primer筆記 泛型演算法

地點 基地 泛型演算法並不直接操作容器,而是遍歷兩個迭代器指定的乙個元素範圍,如此將演算法是作用容器分離,實現通用性。泛型演算法多數定義在標頭檔案algorithm中。比如我們想在vector下找到乙個特定值,可實現如下 include include includeusing namespace ...

C primer筆記 泛型演算法

1 泛型演算法 演算法是因為其實現了一些經典演算法的公共介面,如排序和搜尋。泛型是因為他們可以作用於不同型別的元素和多種容器型別甚至是內建陣列。故稱泛型演算法 2 基本上都定義在algorithm和numeric兩個標頭檔案中,這些演算法遍歷由兩個迭代器指定的乙個元素範圍來進行操作,不對容器進行直接...