C++ lambda表达式
最近在研究高性能异步编程库seastar,其中广泛运用lambda函数,为了更好的理解以及应用seastar框架,在这里对lambda做一个总结。
lambda函数是一个可调用的代码单元,c++可调用对象(callable object)主要分为以下四类:
- 函数
- 函数指针
- lambda
- 重载了函数调用符()的类
语法
[capture list] (parameter list) mumtable throw() -> return type { function body }
主要包含六部分
- 捕获列表
- 参数列表 (可选)
- 可变声明 (可选)
- 异常声明 (可选)
- 返回类型 (可选)
- 函数体
捕获列表
lambda在捕获形式上,分为包括显式捕获和隐式捕获,捕获值的类型上包括值捕获和引用捕获,基本的规则

1 [] 2 [names] #显示捕获列表,以逗号隔开 3 [&] #隐式捕获引用类型 4 [=] #隐式捕获值类型 5 [&, list] #隐式捕获和显示捕获混用,=或&在前,且隐式与显式为不同的类型 6 [=, &list]
引用的一个代码例子

1 struct S { void f(int i); }; 2 3 void S::f(int i) { 4 [&, i]{}; // OK 5 [&, &i]{}; // ERROR 6 [=, this]{}; // ERROR this默认为值 7 [=, *this]{ }; // OK 8 [i, i]{}; // ERROR 重复 9 }
默认情况下,对于一个值被拷贝的变量,此时捕获列表是不允许被修改的(这是因为lambda的调用是const-by-value的),加上mutable则可以修改,此时的lambda为可变lambda

1 #include <iostream> 2 3 int main(){ 4 int v=1; 5 auto y = [v] () mutable{ 6 return ++v; 7 }; 8 std::cout<<y()<<std::endl; // 打印 2 9 }
广义捕获
广义捕获是c++14所支持的一个特性,打破只能捕获lambda作用域变量的局限,将任意表达式作为初始化参数,捕获的新值可以自动推断,举个例子

1 #include <iostream> 2 3 int main(){ 4 int x = 4; 5 6 auto y = [&r = x, x = x + 1]() -> int{ 7 r += 2; 8 return x * x; 9 }(); 10 11 std::cout<<"x: "<<x<<"\n" <<"return: "<<y<<std::endl; //x: 6 return: 25 12 }
r为引用类型,引用了x的值,r+=2之后,因此x值为6;
x=x+1,可以看作x为lambda作用域的一个新的变量,变为5,其平方输出为25。
现在举一个seastar官方的例子

1 seastar::future<int> slow_do_something(std::unique_ptr<T> obj) { 2 using namespace std::chrono_literals; 3 return seastar::sleep(10ms).then([obj = std::move(obj)] () mutable { 4 return do_something(std::move(obj)); 5 }); 6 }
此处便应用了广义捕获,由于unique_ptr不允许拷贝,允许移动,将obj的所有权转移到continuation内,然后obj又将所用权转移到do_something函数内,由于捕获的值是不允许修改,所有加了mutable。
这是seastar需要c++14版本以上的主要原因,coroutine可以更方便有效地支持异步代码,新版本需要c++20,接下来会分享一篇coroutine的主题文章。
参数列表
参数列表是可选的,可以定义成如下形式:

1 auto simple=[]{return 8;};
在c++11时,lambda与函数相同,参数列表类型不允许为auto,避免函数的重载带来歧义。在c++14对此做了改进,因为lambda不会出现重载的情况,支持了参数列表的自动推断auto。

#include <iostream> int main(){ auto y = [] (auto first, auto second){ return first+second; }; std::cout<<y(1,2)<<std::endl; //打印 3 }
引用
lambda还有许多有意思的特性,比如参数模板,constexpr表达式等,详细看参考资料:
https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170
https://en.cppreference.com/w/cpp/language/lambda
https://github.com/scylladb/seastar/blob/master/doc/tutorial.md