简体   繁体   English

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

[英]C++ lambda capture list by value or by reference doesn't give me different results

I am having the below code :我有以下代码:

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]();
}

The output is as expected :输出如预期:

Hello World
Destructing Bar
Out of scope. Sleeping
x:5 y: 10

I am now capturing by value, which is why I assume when the Bar object gets destroyed, I can still access its member variables.我现在按值捕获,这就是为什么我假设当 Bar 对象被破坏时,我仍然可以访问它的成员变量。 If the above is right, I am expecting the below change to give me UB:如果以上是正确的,我期待下面的改变给我 UB:

const auto job = [&](){

However, I still see the same result.但是,我仍然看到相同的结果。 Why is that?这是为什么? Have i understood something wrong?我理解错了吗?

EDIT Further on the above, what I want to understand from this example - is how can I have access to a class member variables in a lambda function even if object has been destroyed?编辑在上面进一步,我想从这个例子中理解 - 即使对象已被破坏,我如何才能访问 lambda 函数中的类成员变量? I am trying to avoid UB and thought that passing by value is the way to go, but can't prove that the opposite isn't working.我试图避免 UB,并认为按价值传递是要走的路,但不能证明相反的方法行不通。

This kind of confusion iss probably one of the reasons why C++20 deprecated the implicit capture of this with [=] .这种混淆可能是 C++20 弃用使用[=]隐式捕获this的原因之一。 You can still capture [this] , in which case you have the usual lifetime issues with an unmanaged pointer.您仍然可以捕获[this] ,在这种情况下,非托管指针通常会出现生命周期问题。 You can capture [*this] (since C+=17), which will capture a copy of *this so you don't have lifetime issues.您可以捕获[*this] (自 C+=17 起),这将捕获*this的副本,因此您不会遇到生命周期问题。

You could also use std::enable_shared_from_this since you're using a std::shared_ptr in example , but that's a bit more complicated.您也可以使用std::enable_shared_from_this ,因为您在example中使用了std::shared_ptr ,但这有点复杂。 Still, it would avoid both the copy and the UB when the lifetime issues.尽管如此,当生命周期出现问题时,它会避免复制和 UB。

In these examples you are capturing this and not any of the fields.在这些示例中,您正在捕获this而不是任何字段。
When capturing this , by design, it is never captured by copying the object or the fields.在捕获this时,根据设计,它永远不会通过复制对象或字段来捕获。
The best way to capture a field by value is:按值捕获字段的最佳方法是:

[field = field] () { }

Both versions of your code have undefined behaviour.您的代码的两个版本都有未定义的行为。 barPtr is the only owner of the shared_ptr so your object is destructed at the end of the scope containing barPtr . barPtrshared_ptr的唯一所有者,因此您的对象在包含barPtr的范围的末尾被破坏。 Executing the lambda which has captured this from the object in barPtr has undefined behaviour.执行从barPtr中的对象捕获this lambda 具有未定义的行为。

The usual way to prevent this is for the lambda to capture a shared_pointer from shared_from_this to keep the object alive.防止这种情况的常用方法是让 lambda 从shared_from_this捕获一个shared_pointer以保持对象处于活动状态。 Eg:例如:

#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]();
}

By capturing self the shared_ptr will now survive for at least as long as the lambda does.通过捕获selfshared_ptr现在将至少与 lambda 一样存活。

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

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