可调用对象有函数,函数指针,lambda表达式,函数对象, bind创建的对象等.
函数和函数指针不必多说.那么什么是函数对象呢?函数对象是一种重载了调用运算符的类类型.因为重载了调用运算符,所以我们可以像调用函数一样调用类的对象,因为称为函数对象.lambda表达式表示一个可调用的代码单元.
函数对象
1 | /** |
以上代码即定义了一个函数对象.调用该对象就像调用函数一样.仿佛该类型对象是一个函数.
还可以定义有数据成员的函数对象
1 | class PrintString { |
lambda表达式
lambda表达式在很多语言中都有.lambda可以理解为一个未命名的内联函数,但是与普通函数不同的是,lambda可能定义在函数内部;lambda表达式必须尾置返回; lambda表达式不能有默认参数; lambda表达式的参数列表和返回类型可以省略. 那么在c++中该怎么定义一个lambda表达式呢?
一个lambda表达式具有以下形式:
1 | [capure list](parameter list)-> return type {function body} |
其中,捕获列表(capure list)是该lambda表达式所在函数中定义的局部变量(通常为空).
参数列表(parameter list), 返回类型, 返回类型(return type)和函数体(function body)与普通函数相同.
当定义一个lambda时,编译器生成一个与lambda对象的新的(未命名的)类类型.当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象.
1 | vector<string> vc{"good", "to", "see", "you", "again"}; |
使用函数对象打印出可变数组vc中的元素.同时定义了一个与函数对象PrintString等价的lambda表达式.
捕获变量
与参数传递类似,捕获变量也有两种方式,值捕获和引用捕获.采用值捕获需要变量可以拷贝.与参数传递不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝.引用捕获需要保证lambda表达式执行时,该引用指向的对象仍然存在.
在一个基本类型为string类型的可变数组中找出第一个长度大于给定值sz的元素.下面分别用函数对象和lambda表达式的形式实现.
1 | class SizeCmp { |
bind参数绑定
标准库的bind函数可以看做一个函数适配器.它接受一个可调用对象,生成一个新的可调用对象.
1 | bool check_size(const string& s, string::size_type sz){ |
可调用对象与function
每一个lambda表达式都是一种未命名的新类型;函数对象的类型是该对象的类类型;函数和函数指针的类型由参数列表和返回值决定.
多个调用对象有不同的类型,但是却共享相同的调用形式.调用形式指明了调用返回的类型以及传递给调用的实参类型(如何SizeCmp类型的函数对象和lambda表达式,类型不同,调用形式相同).一种调用形式对应一个函数类型.标准库中function模板可以表示函数类型.
1 | map<string, function<int(int, int)>> binops = { |
add, Divide(),mod等各个可调用对象的类型各不相同,但是它们都是调用形式为int(int, int)的可调用对象,因此可以赋值给function<int(int,int)>类型的对象.