在协程 lambda (C++) 中捕获“this”是否安全

Is it safe to capture `this` in a coroutine lambda (C++)

提问人:lufinkey 提问时间:8/3/2021 最后编辑:Enlicolufinkey 更新时间:3/26/2023 访问量:663

问:

我一直在使用 c++20 协程,我偶然发现了这个问题,lambda 捕获的生命周期没有延长到协程的整个生命周期。

我想知道什么是安全捕获的,因为我不得不将所有捕获的内容复制到新对象中,如下所示:

[a1=object]() -> task<void> {
    // need to copy into a new object to safely reference for the lifetime of the coroutine
    auto object = a1;
    co_await something;
    // ...

当我在程序中明确捕获时:this

[this]() -> {
    co_await something;
    this->....

我能够在暂停后参考,没有问题。this

但是,在阅读标准时,我发现:

如果某个实体被隐式或显式捕获,但未被复制捕获,则该实体是通过引用捕获的。是的 未指定是否在实体的闭包类型中声明了其他未命名的非静态数据成员 通过引用捕获。

鉴于它是否将指针创建为属性是“未指定”的,这是否意味着我只是幸运?还是捕获有什么不同?this

C++ Lambda 闭包 20 C+ +协程

评论

0赞 Eljay 8/3/2021
只要在对象被销毁后不使用 lambda,在对象被销毁后 lambda 不使用对象,就不会有问题。其他任何事情要么是幸运的(程序立即崩溃),要么是不幸的(程序似乎可以工作)。
1赞 lufinkey 8/3/2021
@Eljay嗯,这和你说的正好相反。使用 C++20 协程时,协程的生存时间比捕获的 lambda 变量更长。所以这有点不同。检查我的第一个链接
1赞 Peter 8/3/2021
如果协程在指向对象的生命周期结束后取消引用指针(例如,调用对象的非静态成员函数),则协程具有未定义的行为。this
1赞 lufinkey 8/3/2021
但是在lambda的寿命结束后呢?由于协程的寿命比 lambda 长。它是否有可能尝试读取存储在 lambda 捕获上的指针地址并导致段错误?this

答:

0赞 Davis Herring 8/4/2021 #1

程序员应该忽略标准中的这句话:它只允许实现为具有引用捕获的 lambda 对象分配比天真预期的内存少(尤其是当调用运算符是内联的时)。

评论

1赞 lufinkey 8/5/2021
我不确定我是否真的理解。我只是试图在我的协程期间不访问死内存。如果我编写一个捕获对象的协程,如下所示,我需要在输入协程时执行对堆栈的复制,如下所示: 否则访问指向死内存。我发现这在捕获时不需要发生。我不明白为什么。标准似乎很不清楚[a1=someObject]() -> task<void> { auto someObject = a1; co_await something; /* attempt to access someObject */ }someObjectthis
1赞 Davis Herring 8/5/2021
@lufinkey:仅通过引用捕获(调用成员函数的对象)。如果该对象处于活动状态,则无论特定调用是否返回,它及其成员都可用。引用本身始终可用(即使它可能悬而未决),因为它是 lambda 的一部分 - 考虑堆栈或实现如何实际找到引用,只是分散注意力。(在所有内容都内联的常见情况下,调用不可能返回,但仍可能使捕获无效。[this]*thisdelete this;[this]
1赞 BeeOnRope 3/5/2022
@DavisHerring - 我不认为“引用本身总是可用的 e(即使它可能悬而未决)因为它是 lambda 的一部分”是真的:lambda 本身可以被销毁,使捕获的值本身无法访问,即,如果您通过引用捕获,例如,它本身可能还活着, 但是在 lambda 内部使用是 UB,因为 labda 本身已被销毁,因此对(可能作为闭包内的指针实现)的引用被销毁。a[&a]()aaa
1赞 BeeOnRope 3/5/2022
由于 lambda 的使用方式,该问题相对频繁地发生,并在第一篇文章中提供的链接@lufinkey进行了解释。所以我认为这个问题可以表述为:在内部声明的局部变量和 lambda 协程的形式参数至少与协程捕获一样长,但 lambda 捕获通常只与 lambda 对象一样长。因此,在协程捕获\
1赞 BeeOnRope 3/23/2022
@DavisHerring - 我有一个更长的回复,但丢失了。我认为我对答案本身(而不是评论)的一个具体抱怨是,它没有回答标题问题“是否安全......”。我认为您仅通过响应标准中令人困惑的文本来暗示的答案是,不,通过重新识别捕获的任何其他捕获都不安全,如 OP 所示:lambda 对象通常会在第一个悬挂点后被破坏,留下悬空(不是在指向被破坏的对象的意义上, 但是thisthisthisthis