C++ 的泛型算法
C++ 提供了许多泛型算法, 部分算法可能需要我们提供一个谓词,谓词是一个可调用的表达式,根据算法的不同接受一个或二个参数,分别称为 一元谓词和二元谓词。 既然是可调用的表达式, 则可以是以下的三种:
- 一般函数 —— 直接传函数名
- Lambda 表达式
- 函数对象
Lambda 表达式
lambda 表达式所表示的就是一个可调用的代码单元,可以理解为未命名的内联函数,使用 Lambda 表达式的关键是理解捕获列表。
声明格式: [ 捕获列表 ] ( 参数列表 ) -> 返回值类型 { 函数体 }
捕获列表可用于传递额外的参数,捕获是指函数体所使用到的外部的变量, 有如下类型:
类型 | 作用 |
---|---|
[=] | 值捕获,类似 const T |
[&] | 引用捕获,类似 T & |
[this] | 所在类成员捕获 |
[v1, v2, … ] | 值捕获指定变量 |
使用 Lambda 表达式时需要知道, 当函数体仅有一个 return 语句时, 编译器可以推算出返回值类型, 除此之外若要返回,都要显示的声明返回值类型。对应值捕获的变量,是不允许修改变量值的, 若要改变, 则使用 mutable 关键字, 如下例:
int a = 0;
auto func = [a]() mutable -> bool { ++a; return a; }
func(); // 返回 1, 外面的 a 保持为 0 。
函数对象 Function objects
函数对象就是重载了 () 的类, 如下例:
class My_cmp
{
bool operator()(int a, int b) { return a < b; }
};
函数对象存在的意义就是, 作为一个类他可以拥有自己的状态,这是其他函数都不能比较的(函数内的静态变量不属于对应函数的状态,而且生存周期不恰当)。
除了自己编写函数对象, functional 也提供了一些内置的函数对象, functional 可以分成两个模块:
- 函数
用于包装并返回函数对象。
bind 可以将函数和参数固定绑定, 见下面的例子:
auto func = [](int a,int b){return a < b;};
auto func_binded = bind(func, placeholders::_1, 10); // _1 是占位符, 表示传给 func_binded 的第一个参数在 func 中的位置。
// func_binded 已经变成一元谓词
cout << func_binded(5) << endl; // 输出 1
-
类
类也可以进一步划分,包装类和操作类(函数对象)- 包装类
- 操作类 —— 都重载了 ()
逻辑和数学运算