跳转到内容

C++/匿名函数

维基教科书,自由的教学读本
< C++

匿名函数,即Lambda函数,可以说是C++11中引入的最重要的特性了。在C++中lambda相当于能自动捕获变量的函数对象的语法糖。即使签名相同的不同函数对象(以及匿名函数)的类型不同,不能相互赋值。因此在将lambda赋值给一个变量时,变量类型只能为auto,因为没法确定lambda的类型。为解决函数对象(以及匿名函数)的类型擦除的复制、传递、存储、调用的问题,C++11引入了std::function

[]{}()可以说是C++中最简洁的表达式了:

  • []中的内容为捕获列表
  • ()中的内容为参数列表。当参数列表为空时,()部分可以省略
  • {}为函数体

有时需要函数能访问和修改一些外部的数据,或者说需要有相应的上下文。函数体与上下文的整体就称作闭包(closure),在Lambda中是用于捕获外部变量。在捕获列表[]中放置函数体中用到的外部变量:

int i = 0, j = 1;
auto f = [i, &j](int a, float b){ ++j; cout << i << ", " << a << ", " << b << endl; };
f(1, 2.0f);

上面这个例子,捕获了i和j两个变量,其中i是值捕获,j是引用捕获。

当捕获的变量较多时,C++还提供了一种指定默认捕获的简便写法:

  • [=]指出默认为值捕获
  • [&]指出默认为引用捕获。需要注意调用lambda函数时捕获对象仍在生命周期内。

指定默认捕获后,其他变量的捕获方式可写在捕获列表后部。下例指定默认为值捕获,j为引用捕获:

auto f = [=, &j](int a, float b){ ++j; cout << i << ", " << a << ", " << b << endl; };

为解决捕获使用C++的移动语义,C++14允许捕获对象由任意表达式初始化。例如:

std::unique_ptr<int> ptr(new int(10));
auto f = [value = std::move(ptr)] { return *value; };

值引用捕获的变量默认是const的,如果要在函数体内修改,需要给lambda加上mutable修饰符:

auto f = [=](int a) mutable { cout << i++ << ", " << a << endl; };

值捕获对象的生命周期与lambda本体的生命周期相同,而引用捕获则通过捕获不同的对象,lambda函数实现了闭包的效果,比如下面的代码就构造了一个斐波那契数列生成器(generator):

如果要明确指出返回类型,可如下例:

auto f = []() -> double { return 5; };