——Lambda表达式)
一、基本用法lambda表达式定义了一个匿名函数并且可以捕获一定范围内的变量。lambda表达式的语法形式简单归纳如下[capture] (params) opt - ret {函数体};其中capture为捕获列表params为参数列表opt为函数选项ret是返回值类型[]捕获一定范围的变量()和普通函数的参数列表一样如果没有参数可以不写opt不需要可以省略返回值类型在C11中lambda表达式的返回值是通过返回值后置语法来定义的函数体和普通函数的函数体一样optmutable: 可以修改按值传递进来的拷贝注意是能修改拷贝而不是值本身exception: 指定函数抛出的异常如抛出整数类型的异常可以使用throw();二、捕获列表lambda表达式的捕获列表可以捕获一定范围内的变量具体使用方式如下[] - 不捕捉任何变量[] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)[] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)拷贝的副本在匿名函数体内部是只读的[, foo] - 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo[bar] - 按值捕获 bar 变量, 同时不捕获其他变量[bar] - 按引用捕获 bar 变量, 同时不捕获其他变量[this] - 捕获当前类中的this指针让lambda表达式拥有和当前类成员函数同样的访问权限如果已经使用了 或者 , 默认添加此选项下面通过一个例子看一下初始化列表的具体用法class Test { public: void output(int x, int y) { auto x1 [] {return m_number; }; // error auto x2 [] {return m_number x y; }; // ok auto x3 [] {return m_number x y; }; // ok auto x4 [this] {return m_number; }; // ok auto x5 [this] {return m_number x y; }; // error auto x6 [this, x, y] {return m_number x y; }; // ok auto x7 [this] {return m_number; }; // ok } int m_number 100; }; x1错误没有捕获外部变量不能使用类成员 m_number x2正确以值拷贝的方式捕获所有外部变量 x3正确以引用的方式捕获所有外部变量 x4正确捕获this指针可访问对象内部成员 x5错误捕获this指针可访问类内部成员没有捕获到变量xy因此不能访问。 x6正确捕获this指针xy x7正确捕获this指针并且可以修改对象内部变量的值注意在匿名函数内部需要通过lambda表达式的捕获列表控制如何捕获外部变量以及访问哪些变量。默认状态下lambda表达式无法修改通过复制方式捕获外部变量如果希望修改这些外部变量需要通过引用的方式进行捕获。三、返回值很多时候lambda表达式的返回值是非常明显的因此在C11中允许省略lambda表达式的返回值。// 完整的lambda表达式定义 auto f [](int a) - int { return a10; }; // 忽略返回值的lambda表达式定义 auto f [](int a) { return a10; };一般情况下不指定lambda表达式的返回值编译器会根据return语句自动推导返回值的类型但需要注意的是labmda表达式不能通过列表初始化自动推导出返回值类型。// ok可以自动推导出返回值类型 auto f [](int i) { return i; } // error不能推导出返回值类型 auto f1 []() { return {1, 2}; // 基于列表初始化推导返回值错误 }四、函数本质使用lambda表达式捕获列表捕获外部变量如果希望去修改按值捕获的外部变量那么应该如何处理呢这就需要使用mutable选项被mutable修改是lambda表达式就算没有参数也要写明参数列表并且可以去掉按值捕获的外部变量的只读const属性。int a 0; auto f1 [] {return a; }; // error, 按值捕获外部变量, a是只读的 auto f2 []()mutable {return a; }; // ok最后再剖析一下为什么通过值拷贝的方式捕获的外部变量是只读的:lambda表达式的类型在C11中会被看做是一个带operator()的类即仿函数。按照C标准lambda表达式的operator()默认是const的一个const成员函数是无法修改成员变量值的。mutable选项的作用就在于取消operator()的const属性。因为lambda表达式在C中会被看做是一个仿函数因此可以使用std::function和std::bind来存储和操作lambda表达式#include iostream #include functional using namespace std; int main(void) { // 包装可调用函数 std::functionint(int) f1 [](int a) {return a; }; // 绑定可调用函数 std::functionint(int) f2 bind([](int a) {return a; }, placeholders::_1); // 函数调用 cout f1(100) endl; cout f2(200) endl; return 0; }对于没有捕获任何变量的lambda表达式还可以转换成一个普通的函数指针using func_ptr int(*)(int); // 没有捕获任何外部变量的匿名函数 func_ptr f [](int a) { return a; }; // 函数调用 f(1314);总结lambda表达式是C11最重要也是最常用的特性之一这是现代编程语言的一个特点lambda表达式有如下的一些优点声明式的编程风格就地匿名定义目标函数或函数对象不需要额外写一个命名函数或函数对象简洁避免了代码膨胀和功能分散让开发更加高效在需要的时间和地点实现功能闭包使程序更加灵活