参考链接:
https://msdn.microsoft.com/zh-cn/library/dd293603(v=vs.110).aspx
https://msdn.microsoft.com/zh-cn/library/dd293608(v=vs.110).aspx
关于C++ Lambda表达式的汇编实现分析:
http://my.oschina.net/ybusad/blog/277445?fromerr=bNPdTMGh
第一次发表博客,请各位路过的有错请批评,也多给点建议:)
概要
C++在C11版本时支持Lambda表达式,至于什么是Lambda? Lambda表达式是一种“匿名函数”,没错它不需要名字,并且可以很自由的让它穿插在各种表达式中,当然也可以让它拥有名字使它变成一个“函数”。这种技术有利有弊,它能使某些工作变得更加灵活,但是也可能使代码变得混乱让人无法。
先体验一下Lambda表达式的使用
int a = 1, b = 2;
int z = [] (int x, int y) -> int { return x + b; } ( a, b ); // 该Lambda表达式计算a和b的和然后赋给z
Lambda表达式的组成
如下图所以Lambda的组成:
(在图中的标注,如下所示:)
- Lambda introduce(外部变量捕获列表)
- 参数声明列表
- mutable描述(不加mutable的话Lambda默认“可理解为const的方法”)
- 异常描述(描述该Lambda表达式可能会抛出什么异常)
- 返回类型
- 复合语句 (Lambda表达式的执行体(函数体))
注:可以把整个Lambda表达式当做是一个函数的名称,“函数名称 + ()”那就是函数调用了。
1. 捕捉列表(Lambda introduce)
“捕捉列表”用于描述在执行体中可以访问那些Lambda所在区域的变量(该Lambda表达式所在的地方要处于该变量的作用域之内)以及怎么访问。
捕捉列表的可能形式(不列出重复的和变形的情形):
[]: 不捕捉Lambda外部的任何变量
[=]: 捕捉外部的所有变量(要处于变量的作用域内)的值
[&]: 捕捉外部的所有变量(要处于变量的作用域内)的引用
[a]: 只捕捉a的值
[a,b]: 只捕捉a、b的值
[&a]: 只捕捉a的引用
[=, &a]: 捕捉a的引用,并捕捉其他外部变量(要处于变量的作用域内)的值
[&,a]: 捕捉a的值,并捕捉其他外部变量(要处于变量的作用域内)的值
例1:
int a = 0, b = 1;
[] () {
cout << a << endl; // 出错,不通过编译
cout << b << endl; // 出错,不通过编译
cout << a << b << endl; // 出错,不通过编译
} ();
由于捕捉列表内什么都没有(即默认情况下)Lambda表达式不捕捉外部的任何变量,所以上面的3条cout调用都不通过编译。
例2:
[a] () {
cout << a << endl; // OK
cout << b << endl; // 不通过编译
}
例3:
ut << a << endl; // 输出0
cout << b << endl; // 输出1
[&a,b] () {
a = 3; // OK
b = 2; // 不通过编译
cout << a << endl; // OK
cout << b << endl; // OK
}
cout << a << endl; // 输出3
cout << b << endl; // 输出1
a是以引用的方式来捕捉的,而b则是直接捕捉它的值,所以如果“能够”在Lambda执行体内改变a、b的值的话,a的改变能够影响到外部变量,但是b就不能了。(那个“能够”是什么意思呢?等到说明mutable字句的时候再说)
“b = 2;”不能通过编译,这是因为Lambda表达式“默认是个const方法”,就像const方法 “不能”去改变 成员变量的值一样这里Lambda也不能改变“捕捉列表”中的变量,但是为什么“a = 3;”能够通过编译呢?这是因为a是个引用,而a = 3;是改变了这个引用指向的变量的值,但是a这个引用本身指向的变量并没有改变。
2. 参数声明列表
就像函数的参数列表一样,这里指的也是Lambda表达式接收的参数列表。
3. mutable描述符
去除Lambda表达式的const特性。
4. throw()
这个字句描述Lambda表达式可能会抛出的异常。
5. 返回值类型字句
Lambda表达式用在参数列表后面指定“-> type”的方式指定Lambda表达式的返回值类型。
例如:
[] (float a, float b) -> float {}; // 该Lambda表达式的返回值类型是float
6. Lambda的执行体
Lambda内“貌似:)”跟函数的用法是一样的。
如何给Lambda表达式一个名字
1. auto
Lambda表达式可以使用auto(C11的新特性,自定判断变量的类型)来作为类型的描述。
例如:
auto lam_hello = [] () { cout << “hello” << endl; };
2. 函数指针
也可以使用取得Lambda的函数指针,但是要注意“捕捉列表”必须为空。
例如:
void (*show) (int n ) = [] ( int n ) { cout << n << endl; };
3. function
这里使用C++标准中的function模板类,需要包含头文件functional,这跟使用函数指针是类似的,不过差别是用这种方法不限定“捕捉列表”是否为空。
例如:
#include <functional>
function<int( int a, int b )> fla = [] ( int a, int b ) -> int { return a + b; };
如何在“自定义函数的参数列表中”表明请提供给我一个Lambda
其实就是在函数的参数列表中指定接收的函数指针。
例如:
void func1( int a, function<void( int n )> l1 ) {
l1( a );
}
void func2( int a, void( *l2 ) ( int ) ) {
l2( a );
}
对于第一种方法,Lambda的捕捉列表可以不为空,但是第二种必须为空。
例如:
int a;
auto l1 = [a] (int x) { cout << x << endl; };
auto l2 = [] (int x) { cout << x << endl; };
// 把下面的l1替换为[a] (int x) { cout << x << endl; }
// l2替换为[] (int x) { cout << x << endl; }也可以
func1(1, l1); // OK
func1(1, l2); // OK
func2(1, l1); // 不能通过编译
func2(1, l2); // OK
Lambda与STL
下面这有一个例子,通过for_each与Lambda的配合把vector内的所有元素显示出来,更多可以转到顶部的超链接看。
vector<int> vt;
for ( int i = 0; i < 20; i++)
vt.push_back(i);
for_each( vt.begin(), vt.end(), [] (int n ) {
cout << n << endl;
} );
关于mutable描述符
首先可以把“捕捉列表”内的变量分为两种:1. &变量(捕捉引用)2. =变量(捕捉值)。
可以把不加mutable的Lambda表达式想象为一个“假象类”内的一个“const方法”称为const_lambda方法,捕捉列表上的变量称为“假象类”的“成员变量”,其中&变量是int& m_a这样的定义,而=变量是int m_b这样的定义。(这种情况是不是真实的?)
那么在const_lambda方法中就不可以修改“=变量”,“&变量”也不可以修改,但是可以修改这个“引用”指向的变量的值。
如果给Lambda表达式加上mutable,就是说把Lambda的const特性给去掉了,const_lambda方法变为mutable_lambda方法。那么现在=变量也可以改变了?
例:
int a = 1, b = 1;
auto lam = [&a,b] () mutable {
cout << a << endl;
cout << b << endl;
a++; // OK
b++; // OK
cout << a << endl;
cout << b << endl;
} ;
lam();
// 输出:
// 1
// 1
// 2
// 2改变了外部变量b的值?
cout << a << endl; // 输出2
cout << b << endl; // 输出1,b并没有被Lambda表达式改变
// 接着上面继续下面的调用:
a = 10;
b = 10;
lam();
// 输出:
// 10
// 2 没有跟随着b = 10的调用而发生改变
// 11
// 3
cout << a << endl; // 输出11
cout << b << endl; // 输出10
这个实验关注的对象是=变量(即捕捉值的变量),可以看到就算给Lambda加上了mutable描述符,照样不对外部相应的变量有任何影响,而且重复调用Lambda,外部变量的变化也没有影响到Lambda表达式内的b。也就是说在Lambda表达式“初始化”的时候使用“捕捉列表”内的变量进行初始化,然后自己来维护这些变量(引用还跟外部变量有关联)。
本文详细介绍了C++ C11中的Lambda表达式,包括其组成部分:捕捉列表、参数声明、mutable描述符、throw()、返回类型和执行体。讨论了如何给Lambda表达式命名,如使用auto、函数指针和std::function。此外,还阐述了Lambda与STL的结合使用,以及mutable描述符对Lambda行为的影响。
577

被折叠的 条评论
为什么被折叠?



