翻譯:李波
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開頭 ...