11 July 2022

C++ 核心指南目录

F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms

理由

为了保障效率和正确,一般会在 lambda 中以引用的形式捕获变量,在 lambda 内局部使用。尤其是编写和调用并行算法时,因为在函数返回时会 join ,捕获了的变量可以在局部进行使用。

讨论

效率性方面,因为对大部分类型来说,引用传参比值传参效率高。

正确性方面,因为不少函数调用会修改对象,产生一些副作用,值传参无法做到。

注意

lambda 不能以 const 引用的方式捕获外部变量。所以也就无法避免在 lambda 内会产生副作用。

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
int main()
{
    int age = 10;
    auto update = [&]() { cout << "update: " << ++age << endl; };
    update();
    cout << "now:    " << age << endl;
}
update: 11
now:    11

以下我们假设 message 是一个很大的网络消息对象,需要传递给迭代算法。如果每次调用都以复制的方式传递,效率不高,且不一定可行,因为消息对象不一定提供复制构造函数。

std::for_each(begin(sockets), end(sockets), [&message](auto& socket)
{
    socket.send(message);
});

例子

以下是一个三个阶段执行的并行流水线。每个阶段对象封装了一个工作线程和一个队列。其析构函数会自动等待队列为空才执行。

void send_packets(buffers& bufs)
{
    stage encryptor([](buffer& b) { encrypt(b); });
    stage compressor([&](buffer& b) { compress(b); encryptor.process(b); });
    stage decorator([&](buffer& b) { decorate(b); compressor.process(b); });
    for (auto& b : bufs) { decorator.process(b); }
}  // automatically blocks waiting for pipeline to finish

强化

警告:如果 lambda 捕获了外部变量的引用,但是在函数作用域外使用。