【发布时间】:2019-05-26 11:42:56
【问题描述】:
我不明白为什么捕获的值会丢失。我了解它与 LambdaWrapper 对象的超出范围或复制有关。但究竟会发生什么?如果 LambdaWrapper(100) 离开 Add 中的范围并且对 __value 的引用丢失,那么为什么与 LambdaWrapper(300) 不同。
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class LambdaWrapper {
public:
LambdaWrapper(double new_value): __value (new_value) {
cout << "constructed with " << __value << endl;
__func = [this](){ cout << __value << endl;};
}
void call() const { __func(); }
private:
double __value;
function<void(void)> __func;
};
class LambdaContainer {
public:
void Add(double value) {
LambdaWrapper w(value); //out of scope
parts.push_back(w);
}
void Add(LambdaWrapper w) // passed as value
{
parts.push_back(w);
}
void call() const {
for (const auto& part : parts)
part.call();
}
private:
vector<LambdaWrapper> parts;
};
int main() {
LambdaContainer c;
c.Add(100);
LambdaWrapper w(200);
c.Add(w);
c.Add( LambdaWrapper(300) ); //LambdaWrapper(300) will out of scope too
cout << "==============" << endl;
c.call();
return 0;
}
输出:
constructed with 100 constructed with 200 constructed with 300 ============== 6.95168e-308 <<< WHY? 200 300
【问题讨论】:
-
考虑当
LambdaWrapper被复制时会发生什么。由于没有用户定义的复制构造函数,__func成员是被复制的__func的精确副本 - 并且它从一个对象中捕获了this指针,当您调用c.call()时,该对象不再存在.因此行为是未定义的。另外:改掉使用带有双下划线前缀的标识符的习惯,例如__func- 这样的标识符是为实现保留的 - 自己创建它们也会导致未定义的行为。 -
为什么使用 300?不小心,因为内存还没有重写?感谢您提供有关双下划线前缀的提示 - 我认为私人成员可以接受它。
-
@EzR1d3r 单下划线在本地被接受。在任何地方都不允许大写和双下划线(我可能会忘记一些事情),包括预处理器宏(快速 gcc 示例 - __reserved)