我们都知道,stl容器中的是有默认的排序函数的,默认排序less<T>
,一般都是按照大小排序的。如果存储的是自定义的元素,并且假设我们的排序因子有多个,则主要是看我们自定义类中的 operator<
函数是怎么书写的。
我们首先看个例子。
#include <iostream>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
class Widget{
public:
Widget(int a, int b) : m_index(a), m_sepeed(b)
{
}
size_t index() const
{
return m_index;
}
size_t maxSpeed() const
{
return m_sepeed;
}
bool operator<(const Widget& rhs) const
{
return this->index() < rhs.index();
}
private:
int m_index;
int m_sepeed;
};
void print(const Widget& w)
{
cout << w.index() << " ";
}
int main()
{
typedef set<Widget> SetVec;
typedef set<Widget>::iterator SetIter;
SetVec setW;
for(int index = 10; index > 0; --index)
{
Widget w(index, (index * 5) / 3);
setW.insert(w);
}
for_each(setW.begin(), setW.end(), print); // 1 2 3 4 5 6 7 8 9 10
return 0;
}
上面的例子中,我们自定义了类Widget
,该类有两个成员变量,index
和 maxSpeed
。operator<
函数是按照index进
行比较。
因此,执行上述的代码,能够看到的结果是从小到大的。但是如果我们想按照最大速度来进行存储呢?就目前我们已经存在的结构是没办法满足我们的需求的。
假设我们需要创建一个按照最大速度排序的容器 Set<Widget>
,我们知道,该容器的默认排序函数用的是less<Widget>
, 而 less<Widget>
在默认情况下是根据 operator<
函数来进行的。
那么,我们是不是可以特化less<Widget>
,切断 less<Widget>
和operator<
函数之间的联系。如下:
template<>
struct std::less<Widget> : public binary_function<Widget, Widget, bool>
{
bool operator()(const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() > rhs.maxSpeed();
}
};
上面这段代码看着就是一个简单的模板特化,但是他特化了一个位于 std
空间中的模板,一般来说,std
空间中的模板是留给标准库使用,而并非开发人员。
尝试对代码进行了编译,不能编译通过。报错:
[Error] specialization of 'template<class _Tp> struct std::less
于是修改代码:
namespace std{
template<>
struct less<Widget> : public binary_function<Widget, Widget, bool>
{
bool operator()(const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() > rhs.maxSpeed();
}
};
}
修改之后,代码能够正常运行,并且也能够按照我们预想的按照元素Widget
的最大速度进行排序。
一般特性之下,对于std
名命名空间的组件进行修改确实是被禁止的,通常这样做会导致未定义的行为,但是在某些特定的情况下,这种情况是被允许的。特别是当程序员可以针对用户自定义的类型,特化std
中的模板。
而在上面的例子中,operator<
不仅仅是 less
的默认实现方式,更是用户期望less
所得事情,而让less
不调用 operator<
而去做别的事,这会无端违背用户的意愿。
而我们经常做的是,我们可以指定一个不同类型的函数子或者函数来代替默认的排序函数子。这也在前面关联容器实现自己的比较函数中已经使用过这样的例子。
而针对上面的例子,最简单的实现方式如下:
struct MaxSpeedCompare : public binary_function<Widget, Widget, bool>
{
bool operator()(const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() > rhs.maxSpeed();
}
};
为了创建以最大速度作为排序因子的容器,我们可以直接使用 MaxSpeedCompare 作为容器的比较函数类型。
typedef set<Widget, MaxSpeedCompare> SetVec;
SetVec setW;
因为所有人都默认使用less时是调用了 operator<
函数做了比较的,因此,我们应该尽量避免修改less
的行为,因为可能会对别人做成误导。
解决办法就是可以自定义一个自己的实现类型,替换默认的排序方式。