cppcoreguidelines-avoid-capturing-lambda-coroutines¶
标记具有非空捕获列表的 C++20 协程 lambda,这些 lambda 可能导致使用后释放错误,并建议避免捕获或确保 lambda 闭包对象具有保证的生存期。
此检查实现了来自 C++ 核心准则的 CP.51。
使用具有非空捕获列表的协程 lambda 可能存在风险,因为捕获变量可能会导致在第一个挂起点之后访问已释放的内存。即使使用引用计数智能指针和可复制类型,也会发生此问题。当 lambda 表达式创建协程时,它会生成一个带有存储空间的闭包对象,该对象通常位于堆栈上,最终会超出范围。当闭包对象超出范围时,其捕获的变量也会超出范围。虽然普通的 lambda 在发生这种情况之前会完成执行,但协程 lambda 可能会在闭包对象被销毁后从挂起状态恢复,从而导致对所有捕获的变量进行使用后释放的内存访问。
考虑以下示例
int value = get_value();
std::shared_ptr<Foo> sharedFoo = get_foo();
{
const auto lambda = [value, sharedFoo]() -> std::future<void>
{
co_await something();
// "sharedFoo" and "value" have already been destroyed
// the "shared" pointer didn't accomplish anything
};
lambda();
} // the lambda closure object has now gone out of scope
在此示例中,lambda 对象定义了两个捕获:value 和 sharedFoo
。当调用 lambda()
时,lambda 对象在堆栈上创建,捕获的变量被复制到闭包对象中。当协程挂起时,lambda 对象超出范围,闭包对象被销毁。当协程恢复时,捕获的变量可能已被销毁,从而导致使用后释放的错误。
总之,当协程在闭包对象被销毁后恢复时,使用具有非空捕获列表的协程 lambda 会导致使用后释放的错误。此检查通过标记具有非空捕获列表的 C++20 协程 lambda 并建议避免捕获或确保 lambda 闭包对象具有保证的生存期来帮助防止此类错误。
遵循这些准则可以帮助确保在 C++ 代码中安全可靠地使用协程 lambda。