假设Widget包含一个重量值和一个最大速度值:
class Widget {
public:
...
size_t weight() const;
size_t maxSpeed() const;
...
};
通常情况下,按重量对Widget排序是最自然的方式:
bool operator<(const Widget& lhs, const Widget& rhs)
{
return lhs.weight() < rhs.weight();
}
假如我们需要创建一个按照最大速度进行排序的multiset<Widget>容器。multiset<Widget>的默认比较函数是less<Widget>,而less<Widget>在默认情况下会调用operator<来完成它的工作。一种方法是特例化less<Widget>,切断less<Widget>和operator<之间的关系,让它只考虑Widget的最大速度:
// 这是std::less对Widget的特化版本;一种不好的做法
template<>
struct std::less<Widget> : public std::binary_function<Widget, Widget, bool> {
bool operator()(const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() < rhs.maxSpeed();
}
};
作为一般性规则,对std名字空间的组件进行修改通常是禁止的,会导致未定义的行为。但在某些特定情况下,对std名字空间的修补工作仍然是允许的,特别是,针对用户定义的类型,特化std中的模板。下面的例子就是Boost库的智能指针shared_ptr的部分实现:
namespace std {
// 针对boost::shared_ptr<T>的std::less特化
template<typename T>
struct less<boost::shared_ptr<T>> : public binary_function<boost::shared_ptr<T>, boost::shared_ptr<T>, bool> {
bool operator()(const boost::shared_ptr<T>& a, const boost::shared_ptr<T>& b) const
{
// shared_ptr::get返回shared_ptr对象的内置指针
return less<T*>()(a.get(), b.get());
}
};
}
这没有什么不合理,上面的less特化只是确保智能指针的排序行为与它们的内置指针的排序行为一致。
让less不调用operator<而去做别的事情,这会无端地违背程序员的意愿,违背了最小惊讶原则POLA(the principle of least astonishment)。
回到那个按照最大速度对multiset<Widget>容器进行排序的例子,你只需要创建一个函数子类,然后让这个函数子类完成你所期望的比较操作:
struct MaxSpeedCompare: public binary_function<Widget, Widget, bool> {
bool operator()(const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() < rhs.maxSpeed();
}
};
我们使用MaxSpeedCompare作为比较类型来创建我们的multiset:
multiset<Widget, MaxSpeedCompare> widgets;
总结:
应该尽量避免修改less的行为,因为这样做很可能会误导其他的程序员,违背POLA程序设计原则。