上篇文章简单介绍了lambda表达式的使用,接下来简述lambda表达式的其他使用方法。
c++11 lambda表达式(一),
lambda表达式对于容器中的算法实现非常方便,在c++11之前仿函数被广泛地用于STL中,同样的,在C++11中,lambda也在标准库中被广泛地使用。由于其书写简单,通常可以就地定义,因此用户常可以使用lambda代替仿函数来书写代码。
下面代码是将 nums 中大于 ubound 变量 的 成员写入 large_nums 中 :
#include
#include
#include
using namespace std;
vector<int> nums = {12, 14, 32, 22, 5, 7, 2, 16};
vector<int> large_nums;
const int ubound = 10;
void print_large_nums()
{
for(const auto &i : large_nums){
cout<< i << " ";
}
cout << endl;
large_nums.clear();
}
inline void large_nums_func(int i)
{
if(i > ubound)
large_nums.push_back(i);
}
void above()
{
//(1) 使用for循环,手写算法
for(auto iter = nums.begin(); iter != nums.end(); ++iter){
if(*iter >= ubound)
large_nums.push_back(*iter);
}
print_large_nums();
//(2) 使用for_each容器算法和函数指针
for_each(nums.begin(), nums.end(), large_nums_func);
print_large_nums();
//(3) 使用for_each容器算法和lambda表达式
for_each(nums.begin(), nums.end(), [=](int i){
if(i > ubound)
large_nums.push_back(i);
});
print_large_nums();
}
int main()
{
above();
return 0;
}
结果显示:

for_each函数用来对容器中的每个元素都调用第三个参数,即function object,可以是一个函数指针、仿函数或者lambda表达式。其中for_each函数原型:
/**
* @brief Apply a function to every element of a sequence.
* @ingroup non_mutating_algorithms
* @param __first An input iterator.
* @param __last An input iterator.
* @param __f A unary function object.
* @return @p __f
*
* Applies the function object @p __f to each element in the range
* @p [first,last). @p __f must not modify the order of the sequence.
* If @p __f has a return value it is ignored.
*/
template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__f(*__first);
return __f; // N.B. [alg.foreach] says std::move(f) but it's redundant.
}
上述三种方式都能完成需求。对于第一中传统的for循环,我认为是代码最清晰的,但是使用for_each算法相较于手写的循环在效率、正确性、可维护性上都具有一定优势。最典型的,程序员不用关心iterator,或者说循环的细节,只需要设定边界,作用于每个元素的操作,就可以在近似“一条语句”内完成循环,正如函数指针版本和lambda版本完成的那样。
因此传统的for循环和for_each算法相比较,应该偏向于使用for_each算法。
而函数指针和lambda表达式,函数指针的方式看似简洁,不过却有很大的缺陷。第一点是函数定义在别的地方,比如很多行以前(后)或者别的文件中,这样的代码阅读起来并不方便。第二点则是出于效率考虑,使用函数指针很可能导致编译器不对其进行inline优化(inline对编译器而言并非强制),在循环次数较多的时候,内联的lambda和没有能够内联的函数指针可能存在着巨大的性能差别。因此,相比于函数指针,lambda拥有无可替代的优势。
同时该算法比较简洁,只在该容器for_each算法中用到,其它地方不会用到,没必要定义成一个普通的函数,同时使用lambda还可以省去函数命名。就地定义,就地使用该匿名函数。
c++有几种可调用的函数对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。
两种不同类型的可调用对象可能共享同一种调用形式,调用形式指明了调用返回的类型以及传递给调用的实参类型。一种调用形式对应一种调用类,比如:
int (int ,int)
是一个函数类型,接受两个int 参数,返回一个int。
对于不同类型的可调用对象共享同一种调用形式的情况,可以看成具有相同的类型。
下面就是用map来定义一个函数表,用于存储指向这些可调用对象的指针。当需要执行某个特定的操作时,从表中查找该调用的函数,如下所示:
#include
#include
#include
#include
using namespace std;
//用户自定义add函数
int add(int i, int j)
{
return i+j;
}
//用户自定义函数对象类
struct divide
{
int operator()(int denominator, int divisor)
{
return denominator/divisor;
}
};
//用户自定义lambad
auto mod = [](int i, int j)
{
return i%j;
};
int main()
{
map<string, function<int (int ,int)>> int_ops = {
{"+", add}, //用户自定义函数指针
{"-", std::minus<int>()}, //标准库中的minus函数对象
{"/", divide()}, //用户自定义的函数对象
{"*", [](int i, int j) {return i * j ;}}, //未命名的lambda
{"%", mod} //命名的lambda
};
cout << int_ops["+"](4, 2) << endl;
cout << int_ops["-"](4, 2) << endl;
cout << int_ops["/"](4, 2) << endl;
cout << int_ops["*"](4, 2) << endl;
cout << int_ops["%"](4, 2) << endl;
return 0;
}
其中function是一个模板,当创建一个具体的类型时,需要提供一些信息,比如:
function<int (int, int)>
声明了一个function类型,它接受两个int参数,返回一个int的可调用对象,使用如下:
#include
#include
#include
#include
using namespace std;
int add(int i, int j)
{
return i+j;
}
struct divide
{
int operator()(int denominator, int divisor)
{
return denominator/divisor;
}
};
auto mod = [](int i, int j)
{
return i%j;
};
int main()
{
function<int (int, int)> fun1 = add;
function<int (int, int)> fun2 = std::minus<int>();
function<int (int ,int)> fun3= divide();
function<int (int, int)> fun4= [](int i, int j) {return i * j ;};
function<int (int, int)> fun5 = mod;
cout << fun1(4, 2) <<endl;
cout << fun2(4, 2) <<endl;
cout << fun3(4, 2) <<endl;
cout << fun4(4, 2) <<endl;
cout << fun5(4, 2) <<endl;
return 0;
}
可以用 map+lambda 的方式来替换难以维护的 if/else/switch,可读性要比大量的分支语句好得多。
lambda 表达式是一个闭包,能够像函数一样被调用,像变量一样被传递;
可以使用 auto 自动推导类型存储 lambda 表达式,但 C++ 鼓励尽量就地匿名使用,缩小作用域;lambda 表达式使用“[=]”的方式按值捕获,使用“[&]”的方式按引用捕获,空的“[]”则是无捕获(也就相当于普通函数);
捕获引用时必须要注意外部变量的生命周期,防止变量失效;
C++14 里可以使用泛型的 lambda 表达式,相当于简化的模板函数。
lambda表达式对于只在某个函数重复的一小块代码段,其它地方也不会用到该代码段,对于这一小块代码段又没必要封装成一个函数,同时避免写重复的代码,lambda表达式在这样的场景十分方便。
匿名的内联函数对象,lambda由类的operator重载而来,闭包,把数据和逻辑打包传递。
C++ Primer中文版
极客时间:罗剑锋的 C++ 实战笔记
深入理解C++11:C++11新特性解析与应用