42.less<T>与operator<

问题实例

 
假设当前存在对象Widget,其具备重量与最高速度属性:

1
2
3
4
5
6
7
class Widget {
public:
...
size_t weight() const;
size_t maxSpeed() const;
...
};

我们默认以重量为排序方式,所以Widget::operator<如下:
1
2
3
bool operator<(const Widget& lhs, const Widget& rhs){
return lhs.weight() < rhs.weight();
}

但如果我们想建立一个按照最高速度排序的multiset<Widget>,而我们又知道默认的比较函数是less<Widget>,而默认的less<Widget>又调用operator<工作。那似乎我们必须特化less<Widget>来切断less<Widget>operator<之间的纽带,让它只关心speed:
1
2
3
4
5
6
template<>//less针对Widget的一个特化
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 namespace里面的模板,虽然std禁止了大部分修改操作,但这种自定义模板特化还是允许的。(Effective C++ 26 也执行了这样的操作)
令less做除operator<以外的事情是对预期行为的无故破坏,就像你不应该把operator+重载成乘法一样。


解决方案

 
STL中没有一个用到less的地方你不能指定一个不同的比较类型。比如说我们可以建立一个名字不叫less的仿函数类:

1
2
3
4
5
6
struct MaxSpeedCompare:public binary_function<Widget, Widget, bool> {
bool operator()(const Widget& lhs, const Widget& rhs) const{
return lhs.maxSpeed() < rhs.maxSpeed();
}
};
multiset<Widget, MaxSpeedCompare> widgets;//自定义比较器

我们不应该切断less<T>operator<的关联性,如果你确有需要,应当在声明容器时同时声明自定义比较器。