[英]Lambda: A by-reference capture that could dangle
有效的現代C ++中的Scott Meyers在lambda章節中說:
請考慮以下代碼:
void addDivisorFilter()
{
auto calc1 = computeSomeValue1();
auto calc2 = computeSomeValue2();
auto divisor = computeDivisor(calc1, calc2);
filters.emplace_back(
[&](int value) { return value % divisor == 0; }
);
}
這段代碼是一個等待發生的問題。 lambda引用局部變量
divisor
,但當addDivisorFilter
返回時,該變量不再存在。 這是在filters.emplace_back
返回后立即執行的,因此添加到filters
的函數在到達時基本上是死的。 使用該過濾器幾乎從創建時開始產生未定義的行為。
問題是:為什么它是一個未定義的行為? 根據我的理解, filters.emplace_back
僅在lambda表達式完成后返回,並且在執行期間, divisor
有效。
更新
我錯過的重要數據包括:
using FilterContainer = std::vector<std::function<bool(int)>>;
FilterContainer filters;
那是因為向量filters
的范圍超過了其中一個函數。 在函數出口處,向量filters
仍然存在,並且捕獲的對divisor
引用現在是懸空的。
根據我的理解,filters.emplace_back僅在lambda表達式完成后返回,並且在執行期間,除數有效。
這不是真的。 向量存儲從閉包創建的lambda,並且不“執行”lambda,在函數退出后執行lambda。 從技術上講,lambda是從一個內部使用引用的閉包(一個依賴於編譯器的類)構造的
#include <vector>
#include <functional>
struct _AnonymousClosure
{
int& _divisor; // this is what the lambda captures
bool operator()(int value) { return value % _divisor == 0; }
};
int main()
{
std::vector<std::function<bool(int)>> filters;
// local scope
{
int divisor = 42;
filters.emplace_back(_AnonymousClosure{divisor});
}
// UB here when using filters, as the reference to divisor dangle
}
在addDivisorFilter處於活動狀態時,您沒有評估lambda函數。 您只是將“函數”添加到集合中,而不知道何時可以對其進行評估(可能在addDivisorFilter返回后很長時間)。
除了@ vsoftco的答案之外,以下修改過的示例代碼可以讓您體驗到這個問題:
#include <iostream>
#include <functional>
#include <vector>
void addDivisorFilter(std::vector<std::function<int(int)>>& filters)
{
int divisor = 5;
filters.emplace_back(
[&](int value) { return value % divisor == 0; }
);
}
int main()
{
std::vector<std::function<int(int)>> filters;
addDivisorFilter(filters);
std::cout << std::boolalpha << filters[0](10) << std::endl;
return 0;
}
此示例在運行時導致Floating point exception
,因為當在main
計算lambda時,對divisor
的引用無效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.