我认为C++11给所有人带来最大的惊喜是引入了Lambda表达式!虽然第二早的高级语言LISP早就引入了这种编程范型,现代语言C#、PHP都提供了Lambda的支持,而最新的Java 8也提供了支持。
一、语法及示例代码:
首先给出Lambda语法定义:
[捕捉列表](参数列表) mutable/const->返回类型{ 函数体 }。
“捕捉列表”有两个作用,其一是告之编译器下面可能是Lambda表达式,其二是捕捉上下文变量供函数体使用。
“参数列表”与普通函数一样,并且在写法上会更简炼:如果没有参数,可以连同()全省略。
“mutable/const”为修饰符,默认情况下为const,mutable可取消常量性。
“返回类型”与一般函数相同,无返回类型时可以与->省略,在返回类型明确的情况下可由编译器推导出。
“函数体”就不用说了,与一般函数完全相同。
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
[]{}; // 这可是最简单的Lambda函数
int x=1, y=2;
// [=]以值的方式捕获所有变量
auto lambda1 = [=] { cout << x << '\t' << y << endl; };
lambda1();
// [&]以引用的方式捕获所有变量
auto lambda2 = [&] { y += x; };
lambda2();
cout << x << '\t' << y << endl;
// x以值y以引用的方式捕获,传入整型参数参与计算,再看函数体是否对x,y产生影响
auto lambda3 = [x, &y](int i) mutable { x += i; y += i; };
lambda3(10);
cout << x << '\t' << y << endl;
// 具有返回值的lambda函数
auto lambda4 = [=]()->int { return x+y;};
cout << lambda4() << endl;
return 1;
}
二、Lambda函数的应用
2.1、STL和Lambda函数
#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> allnums;
vector<int> largenums;
const int base = 100;
inline void SaveLargeNums(int i)
{
if(i>=base)
{
largenums.push_back(i);
}
}
// 基于范围的for循环
inline void Show(void)
{
for(auto val: largenums)
{
cout << val << '\t';
}
cout << endl;
}
inline void Clear(void)
{
largenums.clear();
}
// 仿函数实现
class LargeNums
{
public:
LargeNums(int bs): base(bs){}
void operator()(int i) const
{
if(i>=base)
{
largenums.push_back(i);
}
}
private:
int base;
};
// 无参数选取并显示
void SelectAndShow(void)
{
// 普通for循环
for(auto it=allnums.begin(); it<allnums.end(); ++it)
{
if(*it >= base)
{
largenums.push_back(*it);
}
}
Show();
Clear();
// STL中的for_each结合函数指针
for_each(allnums.begin(), allnums.end(), SaveLargeNums);
Show();
Clear();
// STL中的for_each结合lambda函数
for_each(allnums.begin(), allnums.end(), [=](int i){
if(i>=base)
{
largenums.push_back(i);
}
});
Show();
Clear();
}
// 有参数选取并显示
void SelectAndShow(int bs)
{
// 普通for循环
for(auto it=allnums.begin(); it<allnums.end(); ++it)
{
if(*it >= bs)
{
largenums.push_back(*it);
}
}
Show();
Clear();
// STL中的for_each结合函数指针
/*
for_each(allnums.begin(), allnums.end(), SaveLargeNums);
Show();
Clear();
*/
// STL中的for_each结合仿函数
for_each(allnums.begin(), allnums.end(), LargeNums(bs));
Show();
Clear();
// STL中的for_each结合lambda函数
for_each(allnums.begin(), allnums.end(), [=](int i){
if(i>=bs)
{
largenums.push_back(i);
}
});
Show();
Clear();
// 基于范围的for循环结合lambda函数
for(int i: allnums)
{
auto lambda = [=]{
if(i>=bs)
{
largenums.push_back(i);
}
};
lambda();
}
Show();
Clear();
}
int main()
{
// 初始化
for(int i=0; i<20; i++)
{
allnums.push_back(i*10);
}
// 无参数形式选取
SelectAndShow();
// 有参数形式选取
SelectAndShow(150);
return 1;
}
上面代码可以看出,与STL结合使用中,仿函数与Lambda函数可以匹敌,函数指针在以参数传递时无能为力!for_each比普通的for循环在效率、性能、可扩展性、可维护性方面都要更胜一筹!在不用参数传递时,函数指针不一定被编译器进行inline优化。Lambda函数天生被编译器内联化,在循环量多时,效率不言而喻!
2.2、仿函数与Lambda函数
#include <iostream>
#include <cstdlib>
using namespace std;
class Tax
{
private:
float rate;
int base;
public:
Tax(float r, int b): rate(r), base(b){};
float operator() (float money){ return (money - base)* rate;}
};
int main()
{
float rateL = 0.08, rateM = 0.13, rateH = 0.5;
int baseL = 10000, baseM = 25000, baseH = 40000;
Tax low(rateL, baseL);
Tax Mid(rateM, baseM);
Tax Hig(rateH, baseH);
// 仿函数实现
cout << "tax over 1w " << low(15000) << endl;
cout << "tax over 2.5w " << Mid(30000) << endl;
cout << "tax over 4w " << Hig(42000) << endl;
// Lambda函数实现
auto Llow = [=](int salary){ return (salary - baseL)*rateL;};
auto Lmid = [=](int salary){ return (salary - baseM)*rateM;};
auto Lhig = [=](int salary){ return (salary - baseH)*rateH;};
cout << "tax over 1w " << Llow(15000) << endl;
cout << "tax over 2.5w " << Lmid(30000) << endl;
cout << "tax over 4w " << Lhig(42000) << endl;
return 1;
}
从上面代码可以看出,仿函数与Lambda可以说很相似!C++98中仿函数在STL广泛使用,C++11中用Lambda可以简化仿函数的使用!既然以前有仿函数为何C++11还要提供Lambda函数呢?因为Lambda函数逻辑清晰,且能独立完成相应的功能,以下的代码示例就显示出Lambda函数的优越性。
#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 10;
vector<int> nums;
void Init(void)
{
nums.clear();
for(int i=0; i<N; ++i)
{
nums.push_back(i);
}
}
void Show(void)
{
for(int val: nums)
{
cout << val << '\t';
}
cout << endl;
}
void OneCondition(int val)
{
// 传统的for循环
for(vector<int>::iterator it = nums.begin(); it<nums.end(); ++it)
{
if(*it == val)
{
cout << "find the num in range! " << endl;
break;
}
}
Show();
// 传统的STL方式,equal_to为内置仿函数
if(( find_if(nums.begin(), nums.end(), bind2nd(equal_to<int>(), val))) != nums.end())
{
cout << "find the num in range!" << endl;
}
Show();
// 使用Lambda函数
if(( find_if(nums.begin(), nums.end(), [=](int i){ return i==val;} )) != nums.end())
{
cout << "find the num in range!" << endl;
}
Show();
}
void TwoCondition(int low, int high)
{
// 传统的for循环
for(vector<int>::iterator it = nums.begin(); it<nums.end(); ++it)
{
if(*it>low && *it<high)
{
cout << "find the num in range! " << endl;
break;
}
}
Show();
// 传统的STL方式,除非使用boost库提供的compose2才能完成STL内置less/greater_equal仿函数
/*
if(( find_if(nums.begin(), nums.end(), compose2(logical_and<bool>(),
bind2nd(less<int>(), high),
bind2nd(greater_equal<int>(), low)))) != nums.end())
{
cout << "find the same num!" << endl;
}
*/
// 使用Lambda函数
if(( find_if(nums.begin(), nums.end(), [=](int i){ return (i>low && i<high);} )) != nums.end())
{
cout << "find the num in range!" << endl;
}
Show();
}
void Minus(const int val)
{
// 传统的for循环
for(vector<int>::iterator it = nums.begin(); it<nums.end(); ++it)
{
*it = *it - val;
}
Show();
// 传统的STL方式,equal_to为内置仿函数
for_each(nums.begin(), nums.end(), bind2nd(minus<int>(), val));
Show();
transform(nums.begin(), nums.end(), nums.begin(), bind2nd(minus<int>(), val));
Show();
for_each(nums.begin(), nums.end(), [=](int &i){ i -= val;});
Show();
}
void Complication(int val)
{
for_each(nums.begin(), nums.end(), [=](int &i){
if(i<(-5))
{
i = abs(i);
}
else if(i<(-8))
{
i += val;
}
});
Show();
}
int main()
{
// 初始化
Init();
// 单条件
OneCondition(2);
// 两条件
TwoCondition(4, 9);
// 减操作
Minus(10);
// 复杂操作
Complication(10);
return 1;
}
结果为:
find the num in range!
0 1 2 3 4 5 6 7 8 9
find the num in range!
0 1 2 3 4 5 6 7 8 9
find the num in range!
0 1 2 3 4 5 6 7 8 9
find the num in range!
0 1 2 3 4 5 6 7 8 9
find the num in range!
0 1 2 3 4 5 6 7 8 9
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1
-20 -19 -18 -17 -16 -15 -14 -13 -12 -11
-30 -29 -28 -27 -26 -25 -24 -23 -22 -21
30 29 28 27 26 25 24 23 22 21
以上代码有三个看点:第一:在多条件匹配数据时,STL内嵌的仿函数必须借助于非标准库(如Boost库中的compose2)来完成相应的功能,代码晦涩难懂,没有Lambda函数直观!第二:在对容器数据进行改动时,for_each与内置仿函数minus<int>()已不能完成功能了,该仿函数只是将减的结果返回,没有对容器中数据作修改。只能借助于另一个STL函数:transform,内置仿函数minus<int>()返回的结果将写到第三个参数所给出的首地址当中。第三:在处理复杂运算时,STL库函数已无用武之地了,此时可以说Lambda函数完胜仿函数!另外Lambda函数可以是多参数,除非修改底层STL库,STL内置仿函数在参数上受限。
三、注意事项:
3.1、Lambda函数只能捕捉到所在块的作用域及其父作用域中的自动变量。