仿函数介绍
仿函数(Function Object)是行为类似函数的对象。简单说,就是通过重载operator()
运算符,让类的实例可以像函数一样被调用。
基本示例
class Adder {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Adder add; // 创建一个仿函数对象
int result = add(3, 4); // 像函数一样调用
// result = 7
}
//////////////////////////////////////////////////
class Less//利用仿函数比较大小
{
public:
bool operator()(int x,int y)
{
return x<y;
}
};
int main()
{
Less functor;
cout<<functor(1,2);
}
为什么需要仿函数?
-
可以保存状态:相比普通函数,仿函数可以在多次调用间保持状态
class Counter { int count = 0; public: int operator()() { return ++count; } };
-
可作为模板参数:STL算法常接受仿函数作为参数
std::sort(vec.begin(), vec.end(), MyComparator());
-
类型安全:比函数指针更安全,编译器可以做更多检查
STL中的常见仿函数
#include <functional>
// 算术运算
std::plus<int>()(1,2); // 3
std::minus<int>()(5,3); // 2
// 比较运算
std::greater<int>()(5,3); // true
std::less<int>()(2,3); // true
// 逻辑运算
std::logical_and<bool>()(true, false); // false
非类型模板参数
非类型模板参数是指用常量作为模板参数,在模板中可当作常量使用。与类型参数不同,非类型参数必须是编译期可确定的整型常量、枚举或指针。
关键点:
- 语法:
template<class T, size_t N = 10>
- 限制:浮点数、类对象和字符串不能作为非类型参数
- 应用场景:常用于指定数组大小等编译期确定的常量值
示例:
template<class T, size_t N = 10>
class array {
T _array[N]; // 使用非类型参数N作为数组大小
// ...
};
相对#define
而言,非类型模板参数可以修改,对比#define
更为灵活
模板特化
函数模板特化
当通用模板无法处理某些特殊类型时,可以通过特化提供特殊实现。
特化步骤:
- 必须先有基础模板
- 使用
template<>
声明特化 - 函数名后指定特化类型
- 参数类型必须与基础模板完全一致
示例:
// 基础模板
template<class T>
bool Less(T left, T right) { return left < right; }
// 假设我要比较两个日期的大小,然而传递了日期对象的指针,如果没有进行特化,则会比较地址大小
// 特化版本
template<>
bool Less<Date*>(Date* left, Date* right) { return *left < *right; }
注意: 函数模板特化不如直接重载函数直观,通常建议直接提供重载函数而非特化。
类模板特化
全特化
将模板所有参数都明确指定:
template<>
class Data<int, char> { /*...*/ };
偏特化
部分特化或对参数添加额外限制:
// 部分特化
template<class T1>
class Data<T1, int> { /*...*/ };
// 指针特化
template<class T1, class T2>
class Data<T1*, T2*> { /*...*/ };
// 引用特化
template<class T1, class T2>
class Data<T1&, T2&> { /*...*/ };
应用场景: 当需要对指针或引用等特殊类型进行不同处理时特别有用。