C ++-在Lambda中捕获完美转发的vars

问题描述

我对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保留参数的值类别。但是,当您返回工厂函数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捕获中。如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));
}; 

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...