C++ 理解函数对象与lambda表达式

参考《21天学通C++》第21与第22章节,对函数对象进行介绍,同时通过lambda表达式这一匿名函数对象的简洁方式加深对函数对象的理解。本篇博文的主要内容是:

(1) 函数对象的概念;

(2) 将函数对象用作谓词;

(3) 如何使用函数对象实现一元、二元谓词;

(4) 如何编写lambda表达式;

(5) 如何将lambda表达式用作谓词;

(6) 如何编写可存储和可操作状态的lambda表达式。

一、 函数对象

1. 函数对象的概念和种类

函数对象是C++实体,从概念上讲,函数对象是用作函数的对象;从实现上说,函数对象是实现了operator()的类的对象。虽然函数和函数指针也可视为函数对象,但是吸纳了operator()的类的对象才能保存状态,才能用于标准模板库算法。C++常用于STL算法的函数对象可分为下面两种类型:(1) 一元函数:接受一个参数的函数,如果一元函数返回一个布尔值,称之为谓词;(2)
二元函数:接受两个参数的函数,若返回bool,则称为二元谓词函数。返回bool值得函数对象常常用来进行判断的算法,组合两个函数对象的函数对象称之为自适应函数对象。

2. 函数对象的典型用途

(1) 一元函数的用途

对于STL算法std::for_each()接受三个参数,第一个参数指定范围的起点,第二个参数指定范围的终点,第三个参数是要对指定范围内的每个元素调用的函数。这个函数就可以通过一元谓词实现,举例如下:

template <typename elementType>

struct DisplayElement

{

void operator () (const elementType& element) const

{

cout<< element << ‘ ‘;

}

};

vector<int> vectorElement;

for_each(vectorElement.begin(), vectorElement.end(), DisplayElement<int> ());

这样就可以使用STL算法for_each来对指定范围内的数据执行相同的函数方法。这里也可以使用匿名函数对象,即lambda表达式,如下所示,将上面的for_each改造为相同的功能:

for_each(vectorElement.begin(), vectorElement.end(), [](int& element) { cout << element <<
‘ ‘;});

如果能够使用结构的对象来存储信息,则使用在结构中实现的函数对象的优点将显现出来。

(2) 一元谓词的用途

返回布尔值的一元函数就是谓词,这种函数可用于STL算法的判断。

一元谓词被大量用于STL算法中,例如,std::partition算法使用一元谓词来划分范围,stable_partition算法也使用一元谓词划分范围,但保持元素的相对顺序不变。诸如std::find_if等查找函数以及std::remove_if等删除元素的函数也使用一元谓词,其中std::remove_if删除指定范围内满足谓词条件的元素。

(3) 二元函数的用途

如果函数f(x,y)根据输入参数返回一个值,它将很有用。这种二元函数可用于对两个操作数执行运算,如加减乘除等。同样实现最重要的operator()。在std::transform等算法中,可使用二元乘积函数计算两个容器内容的点乘。

template <typename elementType>

class Multiply

{

public:

elementType operator () (const elmentType& elem1,
const elmentType& elem2)

{ return (elem1 * elem2);}

}

vector<int> vecMultiplicand, vecMultiplier;

vector<int> vecResult;

transform(vecMultiplicand.begin(), vecMultiplicand.end(), vecMultiplier.begin(), vecResult.begin(),
Multiply <int>());

transform将两个范围的内容相乘,并将结果存储在第三个范围中。这里,这三个范围分别存储在类型std::vector的vecMultiplicand、vecMultiplier和vecResult中。乘法运算是通过调用二元函数Multiply::operator
()执行的,对源范围和目标范围内的每个元素都调用了该函数。operator()的返回值保存在vecResult中。

(4)
二元谓词的用途

接受两个参数并返回一个布尔值的函数是二元谓词,这种函数用于诸如std::sort等STL函数中。使用排序二元谓词实现排序,通过实现operator()来动态排序。

很多STL算法都使用二元谓词,比如删除相邻重复元素的std::unique()、排序算法sort、排序并保持相对顺序的std::stable_sort以及两个范围进行操作的std::transform。

(5) 总结

在结构或类中实现函数对象时,它将比简单函数有用的多,因为它也可用于存储与状态相关的信息。谓词是一类特殊的函数对象。

二、 C++ lambda表达式

1. lambda表示概念

可将lambda表达式视为包含公有operator()的匿名结构(或类),从这种意义上说,lambda表达式属于函数对象。从上面所讲到的进行分析:

for_each(vectorElement.begin(),
vectorElement.end(), [](int& element) { cout << element << ‘ ‘;});

编译器加到下述lambda表达式时:[](int&
element) { cout << element << ‘ ‘;}自动将其展开为类似结构DisplayElement<int>的表示:

struct DisplayElement

{

void operator () (const int& element) const

{

cout<< element << ‘ ‘;

}

};

2.
如何定义lambda表达式

以方括号[]打头,告诉编译器,接下来是一个lambda表达式,方括号后面是一个参数列表,该参数列表与不使用lambda表达式时提供给operator()参数列表相同。

3.
一元函数对应的lambda表达式

与一元operator(Type)对应的lambda表达式接收一个参数,定义如下:

[](Type
paramName){ // lambda expression code here}也可用引用来传参:[](Type& paramName){ // lambda expression code here}。

4.
一元谓词对应的lambda表达式

谓词可帮助做出决策,一元谓词是返回bool类型的一元表达式。举例如下:

[](int&
Num){return ((Num % 2) == 0);}

在这里,返回值的性质是为了让编译器知道该lambda表达式的返回类型为bool。在算法中可将lambda表达式用于一元谓词,如可在find_if中使用上述lambda表达式找出集合中的偶数。如果谓词返回true,find_if将返回一个指向相应元素的迭代器,指出找到一个满足条件的元素。

5. 通过捕获列表接受状态变量的lambda表达式

对于上面的一元谓词实现在整数能被2整除时返回true,如果让它更通用,在数字能被用户指定除数整除时返回true,可采用状态变量:

int Divisor = 2;

[Divisor ](int&
Num){return ((Num %
Divisor ) == 0);}

一系列以状态变量的方式传递的参数,也被称为lambda表达式的捕获列表。

6. lambda表达式的通用语法

[StateVar1,
StateVar1](Type& param) { // }

如果要在lambda表达式中修改这些状态变量,可添加关键字multable:

[StateVar1, StateVar1](Type&
param)  multable { // }

这样便可在lambda表达式中修改捕获列表[]中指定的变量,但离开lambda表达式后,这些修改便无效,要确保在lambda表达式内部对状态变量的修改在其外部也有效,应按照引用传递它们:

[&StateVar1, &StateVar1](Type&
param) { // }

lambda表达式还可以接受多个输入参数,为此可用逗号分隔:

[StateVar1, StateVar1](Type1&
param1,Type2& param2) { // }

如果要向编译器明确指明返回类型,可使用->,如下:

[StateVar1, StateVar1](Type1&
param1,Type2& param2)-> ReturnType { return (value or expression); }

最后,复合语句{}可包含多条用分号分割的语句:如下

[StateVar1, StateVar1](Type1&
param1,Type2& param2)-> ReturnType { Statement 1; Statement 2;return (value or expression); }

如果lambda表达式包含多行代码,必须显示地指明返回类型。

7.
二元函数对应的lambda表达式

二元函数接受两个参数,还可返回一个值,与之等价的lambda表达式如下:

[...]
(Type1& param1,Type2& param2){ // }

8.
二元谓词对应的lambda表达式

返回true或false,可帮助决策的二元函数被称为二元谓词函数。这种谓词可用于std::sort等排序算法。

[...]
(Type1& param1,Type2& param2){ return bool expression; }

9.
总结

仅当lambda表达式简洁、高效时才能使用它;

记住lambda表达式总是以[]打头;

除非使用关键字multable进行指定,否则不能修改捕获列表中指定的状态变量;

lambda表达式是实现了operator()的匿名类或结构;

注意使用const对参数进行限定;

lambda表达式的语句块包含多条语句时,要显式的指定返回类型;

不要使用很长的包含多条语句的lambda表达式,而应转而使用函数对象,因为每次使用lambda表达式时,都要重新定义它,这无助于提高代码的重用性。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-17 09:58:00

C++ 理解函数对象与lambda表达式的相关文章

STL函数对象和Lambda表达式

1.基本概念 Function object是定义了operator()的object. FunctionObjectType fo; fo(…);调用函数对象的operator()代替函数fo()的调用. 等价于:fo.operator()(…); 函数对象的三个好处: (1) 函数对象可以有自己的状态,因此可能是更聪明的.你可以拥有同一个函数对象的两个实例,它们可能有不同的状态. (2) 每个函数对象是一个类型.你可以把函数对象作为模版的参数用于指定一个特定的行为. (3)函数对象通常比函数

闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别

闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ console.log("named function") } foo() 不过也可以将函数视作数据赋值给变量,这样的函数可以没有名字: nameless = function(){ console.log("anonymouse function") } nameless(

C# 匿名委托、匿名方法、匿名对象、Lambda表达式

一.匿名类型可通过使用 new 运算符和对象初始值创建匿名类型.示例:var v = new { Name = "Micro", Message = "Hello" };var v = new[] {     new { Name = "Micro", Message = "Hello" },     new { Name = "Soft", Message = "Wold!" }};匿

转载 C#匿名函数 委托和Lambda表达式

转载原出处: http://blog.csdn.net/honantic/article/details/46331875 匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取决于转换的目标类型:如果是委托类型,则转换计算为引用匿名函数所定义的方法的委托:如果是表达式树类型,则转换将计算以对象结构形式表示方法结构的表达式树.  匿名函数有两种语法风格

如何为CriteriaOperator过滤对象转换为lambda表达式,即:linq to xpo的动态where语句

How to convert the CriteriaOperator to a lambda expression, so, the latter expression can be used in the IQueryable source using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using Sys

C++11之lambda表达式

lambda表达式源于函数式编程的概念,它可以就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象.lambda表达式的类型在C++11中被称为"闭包类型",也可以理解为是一个仿函数(带operator()类),其语法形式如下: [capture] (params) opt -> ret {body;}; capture: 捕获列表: params: 参数列表: opt: 函数选项: ret: 返回值类型: body: 函数体. 一个简单的lambda表达式如下:

C++11 lambda表达式学习

lambda表达式是函数式编程的基础.咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下.这里只是介绍C++11中的lambda表达式自己的认识.这里有参考文档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 {

理解Lambda表达式和闭包

了解由函数指针到Lambda表达式的演化过程 Lambda表达式的这种简洁的语法并不是什么古老的秘法,因为它并不难以理解(难以理解的代码只有一个目的,那就是吓唬程序员) 1 #include "stdafx.h" 2 using namespace System; 3 4 typedef void(*FunctionPointer)(System::String ^str); 5 6 void HelloWorld(System::String ^str) 7 { 8 Console:

Python: 高阶函数与lambda表达式

缘由: python语法简单一看就会,但用在实处,想因为少于实战,总感觉有些捉襟. 翻阅跟踪youtube_dl源码,看到filter()函数用法,及其中lambda表达式,感觉好有意思,就补下课,记录所思. 1. 高阶函数 所谓高阶函数,即是能接受函数做参数的函数.函数做参,与c#委托.c++函数指针.Delphi事件有类似之处 比如: def my_func(f, *args): f(args) def my_print(s): print ', '.join(s) my_func(my_p