C++ lambda表达式

最近在研究高性能异步编程库seastar,其中广泛运用lambda函数,为了更好的理解以及应用seastar框架,在这里对lambda做一个总结。

lambda函数是一个可调用的代码单元,c++可调用对象(callable object)主要分为以下四类:

  • 函数
  • 函数指针
  • lambda
  • 重载了函数调用符()的类

语法

[capture list] (parameter list) mumtable throw() -> return type { function body }

主要包含六部分

  1. 捕获列表
  2. 参数列表 (可选)
  3. 可变声明 (可选)
  4. 异常声明 (可选)
  5. 返回类型 (可选)
  6. 函数体 

捕获列表

lambda在捕获形式上,分为包括显式捕获和隐式捕获,捕获值的类型上包括值捕获和引用捕获,基本的规则

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

引用的一个代码例子

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 }
View Code

默认情况下,对于一个值被拷贝的变量,此时捕获列表是不允许被修改的(这是因为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 }
View Code

广义捕获

广义捕获是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 }
View Code

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 }
View Code

此处便应用了广义捕获,由于unique_ptr不允许拷贝,允许移动,将obj的所有权转移到continuation内,然后obj又将所用权转移到do_something函数内,由于捕获的值是不允许修改,所有加了mutable。

这是seastar需要c++14版本以上的主要原因,coroutine可以更方便有效地支持异步代码,新版本需要c++20,接下来会分享一篇coroutine的主题文章。

参数列表

参数列表是可选的,可以定义成如下形式:

1 auto simple=[]{return 8;};
View Code

 在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
}
View Code

 引用

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

posted @ 2022-04-03 23:26  Vincent72143Lau  阅读(56)  评论(0)    收藏  举报