【问题标题】:c++ - capturing perfectly forwarded vars in a lambdac++ - 在 lambda 中捕获完美转发的变量
【发布时间】:2020-12-04 11:02:15
【问题描述】:

我是 C++ 的新手,目前正在为我的第一个项目编写一个控制反转容器,通过在基类上添加注册并将其他参数转发给构造函数来扩展 this blog post

它现在工作得很好,但是当我多次实例化一个 lambda 时,捕获的值似乎被覆盖了。

例子:

struct A{
    short a;
    explicit A(short a_) : a(a_) {}
};
struct IC{
    virtual unsigned C() = 0;
};
struct CImpl : public IC{
    explicit CImpl(unsigned c_) : IC(), c(c_) {}
    unsigned C() override{return c;}
private:
    unsigned c;
};
template<class T, typename...TArgs> 
std::function<T*()> AsMinimalAsItGets(TArgs&&...args)
{ 
    return [&args...]() mutable -> T* 
    { 
        return new T(std::forward<TArgs>(args)...); 
    }; 
} 
auto aFactory = AsMinimalAsItGets<A>(3);
auto cFactory = AsMinimalAsItGets<CImpl>(5);
auto aInst = aFactory();//aInst->a should be 3 but is 5
auto cInst = cFactory();//cInst->C() is 5

A 被实例化为 5 而不是 3。

我尝试this 作为解决方案,但没有解决问题。

那么在实例化 lambda 时如何正确捕获变量? 我需要以一种使我能够在 lambda 中使用完美转发的方式进行捕获

【问题讨论】:

  • &amp;args 成为对时间的参考../ 同时更改为新值

标签: c++ lambda c++17 variadic-templates


【解决方案1】:

当你真正需要一份副本时,不要试图避免。在您的情况下,您尝试通过std::forward 保留参数的值类别。但是当你返回一个工厂函数std::function&lt;T*()&gt; 时,这个闭包必须拥有它用来执行延迟构造的数据。否则,您最终会得到悬空引用,因为传递给 AsMinimalAsItGets 的参数只会超出函数调用的范围。

修复很简单:

template<class T, typename...TArgs> 
std::function<T*()> AsMinimalAsItGets(TArgs&&...args)
{ 
    return [args...]() mutable -> T* 
    //      ^^^^^^^ (1) Copy the arguments into the closure
    { 
        return new T(args...); 
        //           ^^^^^^^ (2) Pass them as is to the ctor
    }; 
} 

请注意,正如@HolyBlackCat 指出的那样,这并不能完美地将参数 转发到 lambda 捕获。如this answer所示,在C++20中,可以

return [...args = std::forward<TArgs>(args)]() mutable -> T* 
{ 
    return new T(args...); 
}; 

在 C++17 中,您需要以下解决方法:

return [args = std::make_tuple(std::forward<TArgs>(args)...)]() mutable -> T* 
{ 
    return std::apply([](auto&&... args){ return new T(args...); },
        std::move(args));
}; 

【讨论】:

  • 这不是完美转发args吗?捕获必须是 ...args = std::forward&lt;TArgs&gt;(args),并且在 lambda 中使用 args 也需要 forward
  • @HolyBlackCat 感谢您的帮助,现在应该修复了。
  • 我想指出 std::make_tuple 不适用于左值引用,它们将被值捕获。我用 std::tuple(std::forward(args)...) 代替,现在它适用于所有值
猜你喜欢
  • 2015-01-06
  • 2015-10-03
  • 2022-07-21
  • 2017-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多