[英]c++ lambda: Currying sum function: returns different results using capture by value vs by reference
[英]C++ lambda capture list by value or by reference doesn't give me different results
我有以下代码:
std::vector<std::function<void()>> functors;
class Bar
{
public :
Bar(const int x, const int y):d_x(x),d_y(y){}
~Bar(){
cout << "Destructing Bar" << endl;
}
void addToQueue()
{
const auto job = [=](){
cout << "x:" << d_x << " y: " << d_y;
};
functors.push_back(job);
}
private :
int d_x,d_y;
};
void example()
{
cout << "Hello World" << endl;
{
shared_ptr<Bar> barPtr = make_shared<Bar>(5,10);
barPtr->addToQueue();
}
cout << "Out of scope. Sleeping" << endl;
usleep(1000);
functors[0]();
}
输出如预期:
Hello World
Destructing Bar
Out of scope. Sleeping
x:5 y: 10
我现在按值捕获,这就是为什么我假设当 Bar 对象被破坏时,我仍然可以访问它的成员变量。 如果以上是正确的,我期待下面的改变给我 UB:
const auto job = [&](){
但是,我仍然看到相同的结果。 这是为什么? 我理解错了吗?
编辑在上面进一步,我想从这个例子中理解 - 即使对象已被破坏,我如何才能访问 lambda 函数中的类成员变量? 我试图避免 UB,并认为按价值传递是要走的路,但不能证明相反的方法行不通。
这种混淆可能是 C++20 弃用使用[=]
隐式捕获this
的原因之一。 您仍然可以捕获[this]
,在这种情况下,非托管指针通常会出现生命周期问题。 您可以捕获[*this]
(自 C+=17 起),这将捕获*this
的副本,因此您不会遇到生命周期问题。
您也可以使用std::enable_shared_from_this
,因为您在example
中使用了std::shared_ptr
,但这有点复杂。 尽管如此,当生命周期出现问题时,它会避免复制和 UB。
在这些示例中,您正在捕获this
而不是任何字段。
在捕获this
时,根据设计,它永远不会通过复制对象或字段来捕获。
按值捕获字段的最佳方法是:
[field = field] () { }
您的代码的两个版本都有未定义的行为。 barPtr
是shared_ptr
的唯一所有者,因此您的对象在包含barPtr
的范围的末尾被破坏。 执行从barPtr
中的对象捕获this
lambda 具有未定义的行为。
防止这种情况的常用方法是让 lambda 从shared_from_this捕获一个shared_pointer
以保持对象处于活动状态。 例如:
#include <vector>
#include <functional>
#include <iostream>
#include <memory>
std::vector<std::function<void()>> functors;
class Bar : public std::enable_shared_from_this<Bar>
{
public :
Bar(const int x, const int y):d_x(x),d_y(y){}
~Bar(){
std::cout << "Destructing Bar\n";
}
void addToQueue()
{
auto self = shared_from_this();
const auto job = [this, self](){
std::cout << "x:" << d_x << " y: " << d_y << "\n";
};
functors.push_back(job);
}
private :
int d_x,d_y;
};
int main()
{
std::cout << "Hello World\n";
{
std::shared_ptr<Bar> barPtr = std::make_shared<Bar>(5,10);
barPtr->addToQueue();
}
std::cout << "Out of scope\n";
functors[0]();
}
通过捕获self
, shared_ptr
现在将至少与 lambda 一样存活。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.