C 0x新特性 詳細講解lambda表示式

2021-05-26 11:39:37 字數 3382 閱讀 3137

翻譯:李波

email: [email protected]

lambda表示式和閉包,part1

c++標準委員會在2023年2月的bellevue

會議上通過了lambda的提議,最新版本的提議與我2023年在這裡展示有很大不同。

術語已經修改了,像語法,語義,使用,以及lambda的實現。在這個系列的第一部分我會介紹最新lambda表示式的概念和原理。

為什麼使用lambda表示式?

乙個lambda表示式(也稱lambda函式)就是乙個定義在呼叫那裡的無名函式。就其本身而言,它和函式物件非常相似。的確,lambda表示式是被自動轉換成函式物件的;那為什麼不直接使用函式物件呢?也許lambda表示式更好用,建立函式物件是非常費勁的:有必須定義乙個類和它的資料成員,乙個過載函式呼叫operator和建構函式。然後你必須在所有呼叫的地方例項化那個型別的物件。這是非常繁瑣的。

為了演示lambda的長處,假設你必須找到第乙個員工,他的工資在給定的範圍內;使用傳統繁瑣的函式物件,你可以寫乙個withinrange class:

class withinrange 

bool operator()(const employee& emp)

};

接下來,使用find_if演算法來定位第乙個工資在指定範圍的員工:

double minimum_salary=1000;

std::find if(employees.begin(), employees.end(),

withinrange(minimum_salary, 1.25* minimum_salary));

find_if第三個引數是乙個函式物件,在其他語言叫做閉包(closure).閉包是乙個儲存被呼叫函式函式環境的無名函式物件,環境由呼叫函式訪問的區域性變數組成;在這個例子中,資料成員low 和 high 是儲存在閉包中的環境;用更簡潔的話說,乙個閉包就是乙個由編譯器根據lambda表示式生成的假設的函式物件;

使用lambda 表示式:

使用新的lambda表示式,上面的find_if可以這樣重寫:

double minimum_salary = 1000;

double upper_limit = 1.25 * minimum_salary;

std::find if(employees.begin(), employees.end(),

[&](const employee& emp) (emp.salary() >= minimum_salary && emp.salary() < upper_limit));

首先,注意

在一行**裡你就寫

l ambda表示式,不再需要另外定義乙個函式。

lambda表示式用作為開始的標誌(我會在以後的系列中討論中&的含義),後面是lambda表示式的引數列表,在這個例子中,引數列表有唯一的引數const employee&組成,整個lambda表示式可以說是單一的,因為它的引數列表被顯式指定的,這裡,單一的引數型別是const employee&,同樣的lambda表示式多型版本可以這樣寫:

[&](emp) (emp.salary() >= minimum_salary && emp.salary() < upper_limit)

這種寫法要求引數型別應該從上下文推導出來(呼叫的地方);當前的標準集中在單一型別的lambda表示式,所以,在這些章節裡我不會討論多型型別的lambda表示式。

隱式和顯式的返回型別

前乙個lambda表示式的最後一部分:

(emp.salary() >= minimum_salary && emp.salary() < upper_limit)

這是乙個lambda表示式的函式體部分。乙個lambda表示式由一對括號組成。在那個例子中,lambda函式的返回型別是從表示式本身隱式推導出來的;例如,下面的表示式產生乙個bool型別結果:

(emp.salary() >= minimum_salary && emp.salary() < upper_limit)

因此,上面lambda表示式的返回型別是bool型別。

從技術上講,如果返回型別沒有顯式的指定,返回型別被定義為decltype(e),e 是在函式體內呼叫的;但你也可以顯示的指定lambda表示式的返回型別;借助新的函式宣告語法,我們可以這樣寫:

[&](emp) ->bool (emp.salary() >= minimum_salary && emp.salary() < upper_limit)

lambda表示式函式體

乙個lambda表示式的函式體可以包含多條語句,在這種情況下,函式體由一對大括號包住(和我們普通的函式體一樣)並且必須有顯式的返回語句;下面的lambda表示式有兩個int型別引數和乙個int型別的返回值,函式體有有三條語句組成和大括號組成:

(int x, int y) -> int

返回語句在這裡是必須的因為lambda表示式有多條語句組成,同樣地,引數列表後的顯示返回型別也是必須指定的。

外部引用

lambda表示式被分為兩個大的種類:無外部引用和有外部引用,後者(外部引用)用來訪問定義在lambd表示式引數列表外的變數,相反的,無外部引用的表示式不訪問定義在lambd表示式引數列表外的變數;

無外部引用的lambda表示式的例子:

(int x, int y) -> int

有外部引用的例子:

int z;

myfunc((int x, int y) -> int );//pseudo code

引用定義在lambda表示式外的區域性變數已經爭論了很長時間了,問題是在lambda函式體內引用的區域性變數必須以某種方式儲存在結果閉包裡,比如前面的這個例子;這些變數是怎樣儲存在閉包裡是爭議的問題;一些人建議使用外部變數的拷貝儲存在閉包裡,然後拷貝在一些情況下是低效的,並有可能導致變數切片(譯者注:比如類)和迭代器失效,其他方案建議儲存這些變數的引用,這種方法也可以是靜態的因為它可能導致懸掛引用。在這個系列第二個部分我會show最新的方案怎麼樣解決外部引用問題和外部引用在閉包怎樣表現的。

第二部分正在翻譯,待續。。。

《C 0x漫談》系列

05年開始關注c 0x,其時c 0x的大部分草案其實都已經初具雛形。但幾個重大的特性 concepts,rvalue,memory model,variadic templates等都還在激烈的動盪當中。於是一路看著這些特性不斷成長,不斷出revisions。其間也跟標準委員會中的一些大牛們,如pe...

《C 0x漫談》系列

05年開始關注c 0x,其時c 0x的大部分草案其實都已經初具雛形。但幾個重大的特性 concepts,rvalue,memory model,variadic templates等都還在激烈的動盪當中。於是一路看著這些特性不斷成長,不斷出revisions。其間也跟標準委員會中的一些大牛們,如pe...

C 0x導讀 2 13 常數

返回目錄 c 0x支援以下7種型別的常數 其中後兩種是c 0x新增的 整型常數 字元常數 浮點常數 字串常數 布林常數 指標常數 自定義常數 1 整型常數 注 因為long long是c 0x才正式加入的,所以目前不同的編譯器對它的支援和標準的描述還有些出入 有三類整型常數 十進位制,以1 9開頭 ...