C++ lambda

转载:http://blog.youkuaiyun.com/xuexiacm/article/details/39835085

lambda表达式:

  1. [capture_block](parameters) mutable exception_specification->return_type{ body }  

lambda表达式包含以下部分:

捕捉块(catpure block): 指定如何捕捉所在作用域中的变量,并供给lambda主体使用。

参数(parameter): (可选)lambda表达式使用的参数列表。只有在不使用任何参数,并且没有自定mutable、一个exception_specification 和一个return_type的情况下可以忽略该列表,返回类型在某些情况下也是可以忽略的,详见对return_type的说明:eg: [] {return 10;}

参数列表和普通函数的参数列表类似,区别如下:

参数不能有默认值。

不允许变长参数列表。

不允许未命名的参数。

mutable:(可选)如果所在作用域的变量是通过值捕捉到,那么lambda表达式主体中可以使用这些变量的副本。这些副本默认标记为const,因此lambda表达式的主体不能修改这些副本的值。如果lambda表达式标记为mutable,那么这些副本则不是const,因此主体可以修改这些本地副本。

exception_specification:(可选)用于指定lambda可以抛出的异常。

return_type:(可选)返回值的类型。如果忽略了return_type,那么编译器会根据以下原则判断返回类型:

如果lambda表达式主体的形式为{return expression;}那么表达式return_type的类型为expression的类型。

其他情况下的return_type为void。

下面的例子演示了如何创建一个lambda表达式并立即执行这个表达式。这行代码定义了一个没有返回值也没有任何参数的lambda表达式。

注意:尾部的(),这对括号使得这个lambda表达式立即执行:

  1. [] {cout << "Hello from Lambda" << endl; } ();  

  1. string result = [](const string & str) -> string { return "Hello from " + str; }("second Lambda");  
  2. cout << "Result: " << result << endl;  
输出如下:
  1. Result: Hello from second Lambda  

根据前面的描述,这个例子中的返回值可以忽略:

  1. string result = [](const string & str){ return "Hello from " + str; }("second Lambda");  

还可以保存lambda表达式的指针,并且通过函数指针执行这个lambda表达式。使用C++11的auto关键字可以轻松地做到这一点:
  1. auto fn = [](const string& str) {return "hello from " + str; };  
  2. cout << fn("call 1") << endl;  
  3. cout << fn("call 2") << endl;  
输出如下:
  1. Hello from call 1  
  2. Hello from call 2  


捕捉块

lambda表达式的方括号部分称为lambda捕捉块(capture block),在这里可以指定如何从所在作用域中捕捉变量。捕捉变量的意思是可以在lambda表达式主体中使用这个变量。有两种方式:

[=]:通过值捕捉所有变量

[&]:通过引用捕捉所有变量

指定空白的捕捉块[]表示不从所在作用域中捕捉变量。还可以酌情决定捕捉那些变量以及这些变量的捕捉方法,方法是指定一个捕捉列表,其中带有可选的默认捕捉选项。前缀为&的变量通过引用捕捉。不带前缀的变量通过值捕捉。默认捕捉应该是捕捉列表中的第一个元素,可以是=或&。

例如:

[&x]只通过引用捕捉x,不捕捉其他变量。

[x]只通过值捕捉x,不捕捉其他变量。

[=, &x, &y]默认通过值捕捉,变量x和y例外,这两个变量通过引用捕捉。

[&, x]默认通过引用捕捉,变量x例外,这个变量通过引用捕捉。

[&x, &y]非法,因为标志符不允许重复。

通过引用捕捉变量的时候,一定保证党lambda表达式在执行的时候,这个引用还是可用的。


将lambda表达式用作返回值

定义在<functional>头文件中的std::function是多态的函数对象包装,类似函数指针。它可以绑定至任何可以被调用的对象(仿函数、成员函数指针、函数指针和lambda表达式),只要参数和返回类型符合包装的类型即可。返回一个double、接受两个整数参数的函数包装定义如下:

  1. function<double(intint)> myWrapper;  
通过使用std::function,可从函数中返回lambda表达式,看一下定义:
  1. function<int(void)> multiplyBy2Lambda(int x)  
  2. {  
  3.     return [=]()->int{return 2 * x; };  
  4. }  

在这个例子中,lambda表达式的返回类型和空参数列表可以忽略,可改写为:
  1. function<int(void)> multiplyBy2Lambda(int x)  
  2. {  
  3.     return[=] {return 2 * x; };  
  4. }  
这个函数的主体部分创建了一个lambda表达式,这个lambda表达式通过值捕捉所在作用域的变量,并返回一个整数,这个返回的整数是传给multiplyBy2Lambda()的值的两倍。这个multiplyBy2Lambda()函数的返回值类型为 function<int(void)>,即一个不接受参数并返回一个整数的函数。函数主体中定义的lambda表达式正好匹配这个原型。变量x通过值捕捉,因此,在lambda表达式从函数返回之前,x值的一份副本绑定至lambda表达式中的x。

可以通过以下方式调用上述函数:

  1. function<int(void)> fn = mutiplyBy2Lambda(5);  
  2. cout << fn() << endl;  
通过C++11的auto关键字可以简化这个调用:
  1. auto fn = mutiplyBy2Lambda(5);  
  2. cout << fn() << endl;  
输出为10。

mutiplyBy2Lambda()示例通过值捕捉了变量x:[=]。假设这个函数重写为通过引用捕捉变量:[&],如下所示。根据代码所示。根据代码后面的解释,下面这段代码不能正常工作:

  1. function<int(void)> mutiplyBy2Lambda(int x)  
  2. {  
  3.     return[&] {return 2 * x; };  
  4. }  

lambda表达式通过引用捕捉变量x。然而,lambda表达式会在程序后面执行,而不会在mutiplyBy2Lambda()函数的作用域中执行,在那里x的引用不再有效。


将lambda表达式用作参数:
您可以编写lambda表达式作为参数的函数。例如,可通过这种方式实现回调函数。下面的代码实现了一个testCallback()函数,这个函数接受一个整数vector和一个回调函数作为参数。这个实现迭代给定vector中的所有元素,并对每个元素调用回调函数,回调函数接受vector中每个元素作为int参数,并返回一个布尔值。如果回调函数返回false,那么停止迭代。

  1. void testCallback(const vector<int>& vec, const function<bool(int)>& callback)  
  2. {  
  3.     for (auto i : vec)  
  4.     {  
  5.         if (!callback(i))  
  6.             break;  
  7.         cout << i << " ";  
  8.     }  
  9.     cout << endl;  
  10. }  
可以按照以下方式测试testCallback()函数。
  1. vector<int> vec(10);  
  2. int index = 0;  
  3. generate(vec.begin(), vec.end(), [&index] {return ++index; });  
  4. for each (vec.begin(), vec.end()m[](int i) {cout << u << " "; });  
  5. {  
  6.     cout << endl;  
  7.     testCallback(vec, [](int i){return i < 6; });  
  8. }  

输出结果:

  1. 1 2 3 4 5 6 7 8 9 10  
  2. 1 2 3 4 5 
<think>好的,我现在需要回答用户关于C++lambda表达式的使用教程。首先,我得回想一下自己对lambda的理解,并参考用户提供的三个引用内容,确保信息准确。用户给的引用里提到了lambda的基本结构、捕获列表、参数和返回类型,还有一些示例代码。我应该先整理这些知识点,分步骤讲解。可能需要从基本语法开始,然后讲捕获方式,再是参数和返回类型,最后举几个例子说明应用场景。要注意用户提到的系统级指令,比如数学公式用$...$,独立公式用$$...$$,但这个问题里可能数学公式不多,不过如果有的话要正确处理。另外,必须用中文回答,结构清晰,分步骤解决,还要生成相关问题。首先,lambda表达式的结构是[捕获列表](参数) mutable->返回类型 {函数体}。需要解释每个部分的作用,特别是捕获列表的不同方式:值捕获、引用捕获,以及隐式捕获=和&的区别。引用中的示例代码展示了这些用法,比如[=]和[a,c]的捕获方式,还有mutable关键字的使用,当需要修改捕获的变量时要用mutable。然后,参数列表和返回类型是可选的,可以自动推导。比如引用中的例子,有的没有参数,有的省略了返回类型。需要指出这些灵活性,并说明在哪些情况下可以省略。接下来,应用场景部分,可能需要举一些常见的例子,比如在STL算法中使用lambda作为谓词,或者用于回调函数、多线程等。引用中的例子主要是在main函数里直接调用,但实际中可能更多用于函数参数,比如std::sort的比较函数,或者std::for_each中的操作。 另外,要注意错误用法,比如捕获局部变量的引用后,变量生命周期结束导致的悬垂引用问题。需要提醒用户注意这一点,避免潜在bug。 最后,生成相关问题的时候,可以围绕lambda的捕获机制、mutable的作用、与函数指针的区别、性能考量等方面提问,确保覆盖用户可能感兴趣的扩展内容。 现在需要组织这些点,确保结构清晰,逐步引导用户理解。同时检查是否有遗漏的重要知识点,比如lambda表达式的类型,它是匿名函数对象,可以赋值给auto变量。还有C++14之后的泛型lambda等新特性,但用户可能只需要基础教程,所以暂时不深入高级内容,除非引用中有提到。但根据提供的引用,似乎都是C++11的内容,所以保持在这个范围内。总结下来,回答的结构可能是: 1.基本语法结构2.捕获列表详解(值捕获、引用捕获、隐式捕获)3.mutable关键字的作用4.参数列表和返回类型的处理5.应用场景示例6.注意事项(如生命周期问题)然后每个部分配合引用中的代码示例,并标注引用标记。例如,在讲解捕获列表时,引用用户提供的第三个引用中的例子,说明如何捕获外部变量a和c。 最后,生成相关问题,比如如何选择捕获方式,lambda与函数对象的比较,mutable的必要性等。</think>### C++ Lambda表达式使用教程 Lambda表达式C++11引入的匿名函数对象,常用于简化代码逻辑、实现回调函数或作为算法参数。以下是其核心用法和注意事项: --- #### 一、基本语法结构 Lambda表达式的基本格式为: $$[捕获列表](参数列表) \text{ mutable} \rightarrow 返回类型 \{ 函数体 \}$$ 其中**捕获列表**和**函数体**不可省略,其他部分可简化[^2]。 **示例**: ```cpp auto add = [](int x, int y) { return x + y; }; // 无捕获,参数为x和y cout << add(1, 2); // 输出3 ``` --- #### 二、捕获列表详解 捕获列表决定了Lambda如何访问外部变量: 1. **值捕获**:`[a, b]`,复制变量值到Lambda内部,默认不可修改(需`mutable`关键字)。 2. **引用捕获**:`[&a, &b]`,直接引用外部变量,可修改原值。 3. **隐式捕获**: - `[=]`:捕获所有外部变量为**值**。 - `[&]`:捕获所有外部变量为**引用**。 **示例**(值捕获与`mutable`): ```cpp int a = 10; auto func = [a]() mutable { a = 100; }; // 修改内部副本,不影响外部a[^1] ``` --- #### 三、参数与返回类型 1. **参数列表**:与普通函数参数类似,可传值或引用。 2. **返回类型**: - 若函数体仅有一个`return`语句,可自动推导返回类型。 - 复杂逻辑需显式指定`-> 返回类型`。 **示例**(自动推导返回类型): ```cpp auto print = [] { cout << "Hello"; }; // 无参数,无返回值 ``` --- #### 四、应用场景 1. **STL算法**:作为谓词函数传递给`std::sort`、`std::for_each`等。 ```cpp vector<int> nums = {3, 1, 4}; sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; }); ``` 2. **异步编程**:在多线程中传递任务逻辑。 3. **简化局部代码**:替代一次性使用的函数对象。 --- #### 五、注意事项 1. **生命周期问题**:引用捕获需确保外部变量在Lambda调用时仍有效。 2. **性能影响**:值捕获可能产生拷贝开销,引用捕获需警惕悬垂引用。 3. **`mutable`限制**:仅允许修改Lambda内部捕获的副本,不影响外部变量。 **错误示例**: ```cpp int* p = new int(5); auto lambda = [&p] { delete p; }; // 可能导致多次释放 lambda(); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值