lambda表达式
C++ 语言中的lambda表达式在很多情况下提供了函数对象的另一种实现机制。Lambda表达式并不是STL所特有的,但它广泛应用于这一环境中。Lambda是表达式是定义一个没有名称、也不需要显示类定义的函数对象。Lambda表达式一般作为一种手段,用来将函数作为实参传递到另一个函数。相比于定义和创建一个常规的函数对象而言,lambda表达式非常容易使用和理解,而且需要的代码也较少。当然,一般而言,lambda表达式并不会取代函数对象。
举个例子,假设有个包含数值的矢量,我们计算此矢量的立方值。可以用transform()函数操作,简单的用lambda表达式完成。
double values[] = {1,2,3,4,5,6};
vector
vector
transform(values.begin(),values.end(),cubes.begin(),[](double x){ return x*x*x;});
最后这条语句用来计算data中的立方值,并存储在cubes。这里简单提一下transform()函数。它是algorithm头文件中的函数,它有两个版本。
第一个版本是将一个一元函数对象指定的操作应用到由一对迭代器指定的一个元素集合上,格式如下:
transform(InputIterator begin, InputIterator end, OutputIterator result,UnaryFuncton f);
transform()的这个版本将一元函数f应用到迭代器begin 和end指定的范围中的所有元素,并从迭代器result指定的位置开始存储结果。Result迭代器可以与begin迭代器相同,只是在这种情况下将会替换原有的内容。这个函数返回一个迭代器,指向存储的最后一个结果的下一个位置。
举例如下:
double values[] = {1,2,3,4,5,6};
vector
transform(values.begin(),values.end(),values.begin(),negate
transform()函数调用negate
transform()第二个版本通过来自迭代器指定的两个范围内的操作数应用一个二元函数。格式为:
transform(InputIterator1 begin1, InputIterator 1end1, InputIterator2 begin2, OutputIterator result,BinaryFunction f);
由begin1和end1指定的范围表示最后一个实参指定的二元函数f的左操作数集合。表示右操作的范围从begin2迭代器指定的位置开始,这个范围不需要提供end迭代器,因为这个范围的元素数量必须与begin1和end1指定的范围元素个数相同,结果从result迭代器位置开始存储在这个范围内。如果希望存回原范围中,result迭代器可以与begin1相同。
举例如下:
复制代码
double values[]={2.5,-3.5,4.5,-5.5,6.5,-7.5};
vector
vector
transform(data.begin(),data.end(),data.begin(),squares.begin(),multiplies
ostream_iterator
copy(squares.begin(),squares.end(),out);
复制代码
Transform()函数通过multiplies
现在回到上文:
transform(values.begin(),values.end(),cubes.begin(),[](double x){ return x*x*x;});
开始的方括号称为lambda引导,它标志着lambda表达式的开始。后面的圆括号中的是lambda的参数列表,这与普通函数相同。此例中只有一个形参x。注意,lambda的参数列表不允许指定形参的默认值,并且参数列表的长度是不可变的。大括号中的是lambda的主体,此例只有一条return语句,当然可以包含多条语句。大家可能注意到这里没有返回类型说明。当lambda表达式的主体是一条单一返回语句,而该语句在lambda表达式主体中返回一个值时,返回类型默认为返回值的类型。否则,返回void。当然可以指定返回类型,如下:
[](double x) ->double{ return x*x*x;} //指定返回double
Capture子句
lambda表达式引导可以包含一个捕获子句,用来确定lambda主体如何访问封闭作用域中的变量。前面lambda表达式方括号之间没有内容,表面封闭作用域没有可以再lambda表达式中访问的变量。若要访问,第一种是方括号之间是 = ,则lambda主体可以按值访问封闭作用域的所有自动变量,但不会修改原始变量。另一中是方括号之间是 & ,则封闭作用域的所有自动变量按应用访问,因此lambda表达式可以修改变量值。例如:
复制代码
double index = 3.0;
double values[] = {1,2,3,4,5,6};
vector
vector
transform(values.begin(),values.end(),cubes.begin(),
[=](double x){ return index*x*x*x;});
复制代码
需要主要的是,这与按值传递实参根本不同,变量index的值可用在lambda中,但不能更新index的副本。如:
transform(values.begin(),values.end(),cubes.begin(),
[=](double x) ->double{
index += 10; // error
return index*x*x*x;});
以上是错误的,若要修改变量的临时副本,则通过添加mutable关键字实现。如:
transform(values.begin(),values.end(),cubes.begin(),
[=](double x)mutable ->double{
index += 10; // ok
return index*x*x*x;});
现在可以修改作用域中的任意变量副本,而不会修改原始值。
transform(values.begin(),values.end(),cubes.begin(),
[&](double x)mutable ->double{
index += 10; // change original value
return index*x*x*x;});
现在采用按引用使用,则会改变index的原始值。
若要捕获一个特定的变量,则:
transform(values.begin(),values.end(),cubes.begin(),
[&index](double x)mutable ->double{
index += 10; // change original value
return index*x*x*x;});
这样,只捕获index,如要捕获多个变量,中间用逗号隔开即可。
Lambda也可以包含throw()异常说明,如:
transform(values.begin(),values.end(),cubes.begin(),
[&index](double x)mutable throw()->double{
index += 10; // change original value
return index*x*x*x;});
如果想要包含mutable说明和throw()说明,则中间必须用一个或多个空格隔开。
现在综合看个实例,用以前说过的函数模板实现。