C++中lambda捕获另一个局部lambda时应选择按值还是按引用捕获?
捕获说明符在lambda上下文的含义
lambda本质是编译器自动生成的匿名类的实例,捕获变量的过程就是为这个匿名类声明对应成员变量的过程,不同捕获方式对应的行为如下:
- 按值捕获:写法为
[aggressiveModel, lazyModel],生成的闭包类会包含两个和外部lambda同类型的成员变量,创建model时会把当前作用域的aggressiveModel、lazyModel两个对象拷贝到成员变量中。你这两个子lambda仅捕获了this指针,拷贝开销只有单个指针大小,几乎可以忽略。 - 按引用捕获:写法为
[&aggressiveModel, &lazyModel],生成的闭包类会包含两个指向外部lambda的左值引用成员,仅绑定到当前函数栈上的两个lambda对象。如果model的生命周期超过当前函数作用域(比如作为返回值传出函数、存入生命周期更长的对象),就会产生悬垂引用,调用时触发未定义行为。 - 默认捕获:分为
[=](默认按值捕获所有用到的外部变量)和[&](默认按引用捕获所有用到的外部变量)两种,不推荐使用:一方面可读性差,其他开发者无法快速判断你实际捕获了哪些变量;另一方面容易误捕获不需要的变量,引发不必要的拷贝或者悬垂问题。
最优方案选择
根据你的使用场景二选一即可:
- 如果
model仅在当前函数内部使用,生命周期不超过当前函数栈帧,按值、按引用捕获都可以,优先选择按值捕获,无悬垂风险,开销可忽略。 - 如果
model需要传出当前函数,或者生命周期长于当前函数栈帧,必须使用按值捕获,完全规避悬垂引用风险。
你给出的示例修改为按值捕获的代码如下:
auto aggressiveModel = [this](const Params& params) -> double { /* 实现逻辑 */ }; auto lazyModel = [this](const Params& params) -> double { /* 实现逻辑 */ }; auto model = [aggressiveModel, lazyModel](const Params& params) { return std::min(aggressiveModel(params), lazyModel(params)); };
如果使用C++14及以上版本,还可以用广义捕获的移动语义进一步降低开销:
auto model = [aggressiveModel = std::move(aggressiveModel), lazyModel = std::move(lazyModel)](const Params& params) { return std::min(aggressiveModel(params), lazyModel(params)); };
内容的提问来源于stack exchange,提问作者Chuu
相关产品推荐
相关产品推荐

