一、问题提出
1.自定义比较函数的sort
我们可以使用自定义的cmp函数、lambda函数或者less< >()、greater< >()
作为自定义compare对象参数传给sort()
函数,达到自定义比较顺序的结果。代码如下:
- 使用自定义cmp函数
bool cmp(const int &a, const int &b){
return a<b;
}
sort(vec.begin(), vec.end(), cmp);
- 使用lambda函数
sort(vec.begin(), vec.end(), [](int a, int b){return a<b;});
- 使用
less< >()
或greater< >()
sort(vec.begin(), vec.end(), less<int>());
2.自定义排序函数的priority_queue
对于priority_queue
(优先队列)可以使用仿函数(函数对象类)自定义compare顺序,代码如下:
class Cmp{
public:
bool operator()(int a, int b){
return a<b;
}
}
priority_queue<int, vector<int>, Cmp> pri_que;
3.问题提出
而自定义sort时并不能使用仿函数名Cmp,例如如下代码是错的:
// 这个代码是错的
class Cmp{
public:
bool operator()(int a, int b){
return a<b;
}
}
sort(vec.begin(), vec.end(), Cmp);
这是为什么呢?
3.什么是仿函数、函数对象类和函数对象
我在查资料时发现网上对仿函数的定义有冲突。
Functors are objects that can be treated as though they are a function
or function pointer.
仿函数(functor)是一个可以被视作函数或者函数指针的对象1。
这段说仿函数是一个对象,可以看做是一个函数或者函数指针。
A functor (or function object) is a C++ class that acts like a function. Functors are called using the same old function call syntax. To create a functor, we create a object that overloads the operator().
仿函数(又叫函数对象)是一个可以像函数一样运行的C++类。调用仿函数与调用函数的语法相同。为了产生一个仿函数,我们需要重载类的"()"运算法1。
这段又说仿函数是一个重载了"()"运算符的类。到底是类还是对象这里也没有说清楚。
而中文搜索的结果大多是:
仿函数又称为函数对象,是一种能够行使函数功能的类。
我个人倾向于认为仿函数是一个类,也叫函数对象类。而将仿函数实例化得到的对象(函数对象)可以看作一个函数或函数指针。下面给出代码举例:
代码举例:
// 这是一个类,仿函数(函数对象类),重载了"()"运算符
class Cmp{
public:
bool operator()(int a, int b){
return a<b;
}
};
//这是一个对象,是实例化后的仿函数对象(函数对象)
Cmp cmp;
bool a = cmp(1,2);//返回值为true,显式使用Cmp类的对象cmp
bool b = Cmp()(1,2);//返回值为true,隐式地使用Cmp类的对象
// bool c = Cmp(1,2); //这段代码错误,因为Cmp是一个类没有经过实例化成一个对象。
二、原因
1.sort源代码
从sort函数的定义如下2:
template< class RandomIt, class Compare >
constexpr void sort( RandomIt first, RandomIt last, Compare comp );
可以看到sort是一个模板函数,这里需要三个输入参数(完整的sort函数并不必须要有三个输入参数,详情见2)。
第一、二个参数是需要排序的迭代器,第三个参数comp是一个Compare类的对象。
2.priority_queue源代码
priority_queue的定义如下3:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
priori_queue是一个模板类,类型参数表为T、Container和Compare,这三者都是类(Class)。我们关注的Compare类默认为std::less< typename Container::value_type>。
3.总结
从上面我们可以知道。对于模板函数sort,我们需要在sort的参数列表"()"中给出自定义的Compare类的对象实例,而对于模板类priority_queue,我们需要在其类型参数表 "<>"中给出自定义的Compare类。
因此使用仿函数作为cmp的合法代码如下:
class Cmp{
public:
bool operator() (int a, int b){
return a<b;
}
};
...
sort(vec.begin(), vec.end(), Cmp()); //这里Cmp()相当于一个类Cmp实例化后的对象
// 或者也可以这样
Cmp cmp;
sort(vec.begin(), vec.end(), cmp); // 这里的cmp是一个Cmp类型的对象
// priori_queue中类型参数表中使用Cmp类名,而不是Cmp类具体实例化后的对象
priority_queue<int, vector<int>, Cmp> pri_que; //
这就是sort和priority_queue在使用仿函数做自定义比较参数时的不同。
至于为什么一个需要 使用实例化后的对象,一个需要使用类名。相信读者也可以看出,因为本质上sort是一个函数,函数参数列表中需要传入实例化后的具体对象,而priority_queue是一个模板类,模板的类型参数表中<>需要传入具体的类,而不是对象。
四、其他
还有一个问题是,priority_queue的类型参数列表中需要传入类名,而lambda函数是一个匿名的类型,所以不能直接使用lambda函数声明priority_queue,需要做些改变,具体的用法可以参考这篇博客,本文中部分内容也参考了该文章。