4 回答
TA貢獻1842條經驗 獲得超13個贊
問題
C ++包含有用的通用函數,例如std::for_each和std::transform,它們非常方便。不幸的是,他們也可以是相當繁瑣的使用,特別是如果函子,你想申請是唯一的特定功能。
#include <algorithm>#include <vector>namespace {
struct f {
void operator()(int) {
// do something
}
};}void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);}如果你只使用f一次并且在那個特定的地方,那么寫一個全班只是為了做一些微不足道的事情似乎有點過分了。
在C ++ 03中,您可能想要編寫類似下面的內容,以保持函數本地:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);}但是這是不允許的,f不能傳遞給C ++ 03中的模板函數。
新的解決方案
C ++ 11引入了lambdas,允許你編寫一個內聯的匿名函子來替換struct f。對于小的簡單示例,這可以更清晰地閱讀(它將所有內容保存在一個地方)并且可能更簡單地維護,例如以最簡單的形式:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });}Lambda函數只是匿名函子的語法糖。
返回類型
在簡單的情況下,lambda的返回類型是為您推導出來的,例如:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);}但是當你開始編寫更復雜的lambda時,很快就會遇到編譯器無法推斷出返回類型的情況,例如:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});}要解決此問題,您可以使用以下方法顯式指定lambda函數的返回類型-> T:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});}“捕獲”變量
到目前為止,我們還沒有使用除了傳遞給lambda之外的任何東西,但我們也可以在lambda中使用其他變量。如果要訪問其他變量,可以使用capture子句([]表達式),這些子句在這些示例中尚未使用,例如:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});}您可以通過引用和值捕獲,您可以分別使用&和指定=:
[&epsilon]通過引用捕獲[&]通過引用捕獲lambda中使用的所有變量[=]按值捕獲lambda中使用的所有變量[&, epsilon]捕獲變量,如[&],但epsilon值[=, &epsilon]捕獲變量,如[=],但epsilon通過引用
默認情況下生成的operator()是const隱式,默認情況下,const當您訪問它們時捕獲將是。這具有以下效果:具有相同輸入的每個調用將產生相同的結果,但是您可以將lambda標記為mutable請求operator()生成的不是const。
TA貢獻1795條經驗 獲得超7個贊
Lambda表達式通常用于封裝算法,以便將它們傳遞給另一個函數。但是,可以在定義時立即執行lambda:
[&](){ ...your code... }(); // immediately executed lambda expression在功能上等同于
{ ...your code... } // simple code block這使得lambda表達式成為重構復雜函數的強大工具。首先將代碼段包裝在lambda函數中,如上所示。然后可以在每個步驟之后通過中間測試逐漸執行顯式參數化的過程。一旦您完全參數化了代碼塊(如刪除所示&),您可以將代碼移動到外部位置并使其成為正常功能。
同樣,您可以使用lambda表達式根據算法的結果初始化變量 ...
int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!作為一種分區程序邏輯的方法,您甚至可能會發現將lambda表達式作為參數傳遞給另一個lambda表達式很有用......
[&]( std::function<void()> algorithm ) // wrapper section
{
...your wrapper code...
algorithm();
...your wrapper code...
}([&]() // algorithm section
{
...your algorithm code...
});Lambda表達式還允許您創建命名嵌套函數,這可以是避免重復邏輯的便捷方法。當將非平凡函數作為參數傳遞給另一個函數時,使用命名的lambdas在眼睛上也會更容易(與匿名內聯lambda相比)。 注意:關閉大括號后不要忘記分號。
auto algorithm = [&]( double x, double m, double b ) -> double
{
return m*x+b;
};int a=algorithm(1,2,3), b=algorithm(4,5,6);如果后續分析顯示函數對象的顯著初始化開銷,您可以選擇將其重寫為普通函數。
TA貢獻1797條經驗 獲得超6個贊
答案
問:C ++ 11中的lambda表達式是什么?
答:在引擎蓋下,它是一個帶有重載operator()const的自動生成類的對象。這種對象稱為閉包,由編譯器創建。這個'閉包'概念接近于C ++ 11中的綁定概念。但是lambdas通常會生成更好的代碼。通過閉包調用允許完全內聯。
問:我什么時候使用?
答:定義“簡單和小邏輯”并要求編譯器執行上一個問題的生成。你給編譯器一些你想要在operator()中的表達式。編譯器將為您生成所有其他東西。
問:他們解決了哪些問題在引入之前是不可能的?
答:這是某種語法糖,比如運算符重載而不是自定義添加,子作用操作的函數......但它保存了更多不需要的代碼行,將1-3行真實邏輯包裝到某些類等等!一些工程師認為,如果線的數量較少,那么在其中產生錯誤的機會就會減少(我也這么認為)
用法示例
auto x = [=](int arg1){printf("%i", arg1); };void(*f)(int) = x;f(1);x(1);關于lambdas的額外內容,未提及問題。如果您不感興趣,請忽略此部分
1.捕獲的價值。你可以捕獲什么
1.1。您可以在lambdas中引用具有靜態存儲持續時間的變量。他們都被抓獲了。
1.2。您可以使用lambda“按值”捕獲值。在這種情況下,捕獲的變量將被復制到函數對象(閉包)。
[captureVar1,captureVar2](int arg1){}1.3。你可以捕獲參考。& - 在這種情況下意味著參考,而不是指針。
[&captureVar1,&captureVar2](int arg1){}1.4。它存在通過值或引用捕獲所有非靜態變量的符號
[=](int arg1){} // capture all not-static vars by value
[&](int arg1){} // capture all not-static vars by reference1.5。它存在通過值或通過引用捕獲所有非靜態變量并指定smth的表示法。更多。示例:按值捕獲所有非靜態變量,但通過引用捕獲Param2
[=,&Param2](int arg1){}通過引用捕獲所有非靜態變量,但通過值捕獲Param2
[&,Param2](int arg1){}2.退貨類型扣除
2.1。如果lambda是一個表達式,則可以推導出Lambda返回類型?;蛘吣梢悦鞔_指定它。
[=](int arg1)->trailing_return_type{return trailing_return_type();}如果lambda有多個表達式,則必須通過尾隨返回類型指定返回類型。此外,類似的語法可以應用于自動函數和成員函數
3.捕獲的值。什么你無法捕捉
3.1。您只能捕獲本地變量,而不能捕獲對象的成員變量。
4.Сonversions
4.1 !! Lambda不是函數指針,它不是匿名函數,但可以將無捕獲的 lambdas隱式轉換為函數指針。
PS
有關lambda語法信息的更多信息,請參閱編程語言C ++#337,2012-01-16,5.1.2的工作草案。Lambda表達式,第88頁
在C ++ 14中,添加了名為“init capture”的額外功能。它允許對閉包數據成員進行仲裁聲明:
auto toFloat = [](int value) { return float(value);};
auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
- 4 回答
- 0 關注
- 1362 瀏覽
添加回答
舉報
