C++11 Lambda 表达式

Lambda 表达式是重载了函数调用运算符 () 的未命名类的未命名对象。
Lambda 表达式可以被认为是一个匿名的函数对象
与任何函数类似一个 Lambda 表达式包含一个返回类型、一个参数列表、一个函数体。

01 Lambda 定义

[captuer_list](parameter_list) -> return_type { function_body }
  • captuer_list捕获列表,是一个 Lambda 表达式所在函数中定义的局部变量的列表
  • return_type尾置返回,是一个 Lambda 表达式的返回类型,如果想要显式的声明返回值的类型则必须使用这种形式
  • Lambda 表达式必须包含捕获列表函数体

02 向 Lambda 表达式传递参数

默认情况下,从 Lambda 表达式生成的类都包含一个对应该 Lambda 表达式所捕获的变量的数据成员

类似任何普通类的数据成员, Lambda 表达式的数据成员也在 Lambda 表达式对象创建时被初始化,即被捕获的变量值是在 Lambda 表达式创建时拷贝,而不是调用时拷贝。

void func() {
    int n = 10;
    auto func = [n]() { return n; };    // n 在创建时被拷贝到 Lambda 表达式中
    n = 0;                              // 改变外部的值不会影响到 Lambda 中的拷贝
    auto m = func();                    // 调用时 Lambda 表达式中 n 的拷贝的值为 10
    cout << m << endl;				    // 10
}

Lambda 表达式不能有默认参数,Lambda 表达式调用的实参数目永远和形参数目相等

Lambda 的捕获方式

语法说明
[]空捕获列表。Lambda 不能使用所在函数中的变量
[names]names 是一个以 , 分隔开的名字列表,这些名字的都是 Lambda 表达式所在函数的局部变量。默认情况下捕获列表中的变量都是拷贝
[&]隐式捕获列表,采用引用捕获方式。Lambda 表达式中所使用的来自所在函数的实体均采用引用的方式
[=]`隐式捕获列表,采用值捕获方式。Lambda 表达式中所使用的来自所在函数的实体均采用值传递的方式
[&, identifier_list]identifier_list 是一个 , 分隔的列表,包含 0 个或多个来自所在函数的变量,且这些变量均采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获, identifier_list 中的名字前面不能再使用 &
[=, identifier_list]identifier_list 是一个 , 分隔的列表,包含 0 个或多个来自所在函数的变量,且这些变量均采用引用捕获方式,而任何隐式捕获的变量都采用引用方式捕获, identifier_list 中的名字不能包含 this 且这些名字之前必须使用 &
  • 隐式捕获是为了让编译器根据 Lambda 体中的代码来推断可能要使用到的变量。
  • 当混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是 &=, 此符号制定了默认捕获方式的类型。
  • 当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。
    // 捕获举例
    void func() {
        int n = 10;
        // 值捕获
        auto func1 = [n]() { return n; };
        // 引用捕获
        auto func2 = [&n]() { return n; };
    
        n = 0;
        auto p = func1();		            // p = 10
        auto q = func2();		            // q = 0
    }
    

03 可变 Lambda

  • 默认情况下,对于一个值被拷贝的变量, Lambda 不会改变其值。
    如果我们希望改变一个被捕获的变量的值,就必须再参数列表首加上关键字 mutable, 且可变 Lambda 能省略参数列表

    void func() {
        int n = 10;
        auto func1 = [n]() { return ++n; };             // error: 无法在非可变 lambda 中修改通过复制捕获
        auto func2 = [n]() mutable { return ++n; };     // ok
    }
    
  • 引用捕获的变量是否可以修改取绝于此引用指向的是一个 const 还是一个非 const 类型。

    void func() {
        int n = 10;
        const int m = 81;
        auto func1 = [&n]() { return ++n; };	
        auto func2 = [&m]() { return ++m; };            // error: 无法在非可变 lambda 中修改通过复制捕获
        auto func3 = [&m]() mutable { return ++m; };    // error: 左值指定 const 对象
    }
    

04 指定 Lambda 返回类型

默认情况下,如果一个 Lambda 体包含 return 之外的任何语句,则编译器会假定此 Lambda 体返回 void.
当需要为 Lambda 定义返回类型时,必须使用尾置返回类型

int n = -10;
auto func1 = [](int i) { return i > 0 ? i : -i; };	
auto func2 = [](int i) { if (i > 0) return i; else return -i; };
auto func3 = [](int i) -> decltype(i) { if (i > 0) return i; else return -i; };

05 使用 std::function 存储 Lambda 表达式

std::function 可以存储 Lambda 表达式。

std::function 还可以绑定普通函数、静态成员函数和共有成员函数。前两者的使用方法和绑定 Lambda 表达式的方法一样:直接将函数名赋值给 function 对象即可;但类的成员函数需要使用 bind 绑定:

MyClass *obj = new MyClass();
// 使用 bind 绑定成员函数和对象指针
// 使用 placeholders 占位符来表示函数的参数数量,其后缀一次为 1~N
std::function<void(int)> func1 = std::bind(&MyClass::testFunc1, obj, std::placeholders::_1);
std::function<void(int, char)> func2 = std::bind(&MyClass::testFunc2, obj, std::placeholders::_1, std::placeholders::_2);
### C++11 Lambda表达式使用教程 #### 定义与基本语法 C++11引入了lambda表达式这一强大特性,使得开发者能够更加便捷地定义和创建匿名函数。这种新的功能不仅简化了代码编写过程,还提升了程序的功能性和执行效率[^1]。 Lambda表达式的通用形式如下: ```cpp [capture](parameters)->return_type { body } ``` 其中`capture`表示捕获列表,用于指定如何访问外部作用域中的变量;`parameters`代表参数列表;`->return_type`可选部分用来显式声明返回类型;最后是函数体`body`[^2]。 #### 捕获列表详解 捕获列表允许lambda表达式获取其所在环境内的局部变量或全局变量。常见的几种方式包括但不限于按值传递(`[var]`)、按引用传递(` [&var] `),以及默认全部按值/引用捕获(` [= ] / [&] `)[^3]。 ##### 示例:按值捕获 当采用按值的方式捕获时,意味着会复制一份原始数据供内部逻辑操作而不影响原值。 ```cpp #include <iostream> using namespace std; int main(){ int value = 42; auto func_by_value = [value]() { cout << "Value captured by copy: " << value << endl; }; ++value; // 修改外界的value func_by_value(); // 输出的是未修改前的数值 } ``` ##### 示例:按引用捕获 相反地,如果选择了按引用的形式,则任何对于被捕获变量的操作都会直接影响到实际存在的那个实例。 ```cpp #include <iostream> using namespace std; int main(){ int ref_val = 88; auto modify_ref = [&ref_val]() mutable{ ++ref_val; cout << "Modified reference-captured variable to : " << ref_val << endl; }; modify_ref(); } ``` #### 实际应用场景举例 考虑这样一个场景——我们需要交换两个整型数的位置而无需借助额外的空间开销。此时可以巧妙运用带有适当捕获机制的lambda表达式来完成任务[^5]。 ```cpp void swapTwoNumbers(int &num1, int &num2){ auto swapper = [&](int &a, int &b){ int temp=a;a=b;b=temp; }; swapper(num1,num2); } // 或者更为简洁的做法直接在lambda体内处理 auto direct_swap=[&](int &x,int &y){swap(x,y);} direct_swap(a,b); ``` #### 关于赋值行为的重要注意事项 值得注意的一点在于,尽管可以通过拷贝构造的方式来生成另一个具有相同行为的新对象,但是不同lambda表达式之间不允许互相赋值,即便它们看起来拥有完全一致的结构和语义[^4]。 ```cpp auto hello_world_1=[](){std::cout<<"Hello World"<<std::endl;}; auto hello_world_2=[](){std::cout<<"Hello World"<<std::endl;}; // 下面这行会导致编译错误 //hello_world_1=hello_world_2; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值