c++ - 在 lambda 中捕获完美转发的变量

c++ - capturing perfectly forwarded vars in a lambda

我是 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

中使用完美转发的方式进行捕获

当您确实需要一份副本时,不要试图避免副本。在您的情况下,您尝试通过 std::forward 保留参数的值类别。但是当你 return 一个工厂函数 std::function<T*()> 时,这个闭包必须拥有它用来执行延迟构造的数据。否则,您最终会得到悬空引用,因为传递给 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 捕获。如所示,在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));
};