初探C++11之lambda表达式
lambda表达式是函数式编程的基础。咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下。这里只是介绍C++11中的lambda表达式以及与此相关的闭包(closure)。
同样,这里首先给出参考文档
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf
其次,给出两个例子,可以看出lambda表达式的写法
[](int x, int y) { return x + y; }
[](int x, int y) -> int { int z = x + y; return z + x; } //表明返回值类型是int型
也就是说,一个lambda表达式很类似于普通的函数定义的写法,区别在于三点,一是没有函数名(你也可以认为函数名是[]),二是这个函数没有普通函数那样的返回值类型,返回值类型的写法在第2行,即在参数列表与函数定义之间以箭头写明,三是在参数列表之前有一个[] (其实这里还可能有更多的形式,后面会说)
第三,lambda表达式的标准形式是什么?
如下。这里先只是列出,后面会详细解释。
lambda-expression:
lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
[ lambda-captureopt ]
lambda-capture:
capture-default
capture-list
capture-default , capture-list
capture-default:
&
=
capture-list:
capture
capture-list , capture
capture:
identifier
& identifier
this
lambda-declarator:
( parameter-declaration-clause ) attribute-specifieropt mutableopt exception-specificationopt trailing-return-typeopt
第四,进一步的分析
上面的标准形式中,最不好理解的可能就是lambda-capture了。其实它对应的就是闭包的概念,而作用是描述外部传入的参数。
wiki上有一个例子,如下
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x;
});
std::cout << total;
这段代码做的事情是把someList中的所有变量求和。其中定义的lambda函数如下,
[&total](int x) {total += x;}
与前面的例子不同,这里的lambda-introducer是[&total]而不是之前的[]。这么写的作用是,在这个lambda函数中以引用方式使用外部变量total,这样求和结果就可以存放于这个变量中。那么类似的,还可以以传值方式使用外部变量,写成[total]就可以,而且,如果需要用到的外部变量较多,可以有简略的写法。一个完整的列表如下(来自wiki,这里我把他们翻译成我们比较习惯的表述形式)
[] // 没有定义任何变量。使用未定义变量会导致错误。
[x, &y] // x 以传值方式传入(默认),y 以引用方式传入。
[&] // 任何被使用到的外部变量皆隐式地以引用方式加以使用。
[=] // 任何被使用到的外部变量皆隐式地以传值方式加以使用。
[&, x] // x 显示地以传值方式加以使用。其余变量以引用方式加以使用。
[=, &z] // z 显示地以引用方式加以使用。其余变量以传值方式加以使用。
第五,代码练习及验证
接下来,我们自己写一些代码来做一些练习以及验证一些事情。以下代码在visual studio 2010下测试通过。
首先是模仿实例写一些代码。
#include <iostream>
#include <algorithm>
#include <vector>
void ShowVector(std::vector<int>& vecTest)
{
std::for_each(vecTest.begin(), vecTest.end(),
[](int x)
{
std::cout<<x<<' ';
}
);
std::cout<<std::endl;
}
int main()
{
std::vector<int> vecTest(10,1);
ShowVector(vecTest);
std::for_each(vecTest.begin(), vecTest.end(),
[](int& x)
{
x += 2;
}
);
ShowVector(vecTest);
int iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[&iTotal](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[&](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
//iTotal = 0;
//std::for_each(vecTest.begin(), vecTest.end(),
// [=](int x)
// {
// iTotal += x; // build error,error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
// }
//);
//std::cout << std::endl << iTotal << std::endl;
//iTotal = 0;
//std::for_each(vecTest.begin(), vecTest.end(),
// [iTotal](int x)
// {
// iTotal += x; // build error, error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
// }
//);
//std::cout << std::endl << iTotal << std::endl;
iTotal = 0;
std::for_each(vecTest.begin(), vecTest.end(),
[=, &iTotal](int x)
{
iTotal += x;
}
);
std::cout << std::endl << iTotal << std::endl;
int iAdd = 1;
std::for_each(vecTest.begin(), vecTest.end(),
[=](int& x)
{
x += iAdd;
}
);
ShowVector(vecTest);
std::for_each(vecTest.begin(), vecTest.end(),
[iAdd](int& x)
{
x += iAdd;
}
);
ShowVector(vecTest);
char e = 0;
scanf_s("%c", &e, 1);
return 0;
}
运行结果如下,
1 1 1 1 1 1 1 1 1 1
3 3 3 3 3 3 3 3 3 3
30
30
30
4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5
这里值得注意的是其中的注释掉的代码,这里我们以传值方式使用外部变量,但是在lambda函数内部却试图修改该变量的值,其实这是没什么作用的,而visual studio直接给出报错,检查很仔细,不错。
那么我们再考虑一个问题,lambda表达式的运行效率?我们来试一下。测试机器双核4G内存。
#include "windows.h"
#include <iostream>
#include <algorithm>
#include <vector>
void ModVal(int& iVal)
{
iVal *= 3;
}
#define ARRAY_SIZE 10000000
int main()
{
int m = 0;
std::vector<int> vecTest(ARRAY_SIZE,0);
std::for_each(vecTest.begin(), vecTest.end(),
[&m](int& x)
{
x = m++;
}
);
// test1
//std::for_each(vecTest.begin(), vecTest.end(),
// [](int& x)
// {
// x *= 3;
// }
//);
// test2
//for (int i = 0; i < ARRAY_SIZE; i++)
//{
// vecTest[i] *= 3;
//}
// test3
std::for_each(vecTest.begin(), vecTest.end(), ModVal);
char e = 0;
scanf_s("%c", &e, 1);
return 0;
}
分别对以上的test1, test2和test3使用QueryPerformanceFrequency()函数计时,我们可以得到如下的结果,
test1: 26.8ms
test2: 27.6ms
test3: 24.2ms
也就是说,最快的是test3,即不采用lambda表达式,而是直接用函数……多少有点出乎意料。而最慢的是直接用for循环的操作,不过它和用lambda表达式的结果很接近。
第六,其他
成员函数内部的lambda函数,lambda函数作为对象。留着后面写。