繁体   English   中英

C++ lambda 按值或按引用捕获列表并没有给我不同的结果

[英]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] () { }

您的代码的两个版本都有未定义的行为。 barPtrshared_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]();
}

通过捕获selfshared_ptr现在将至少与 lambda 一样存活。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM