这不是太难。首先,考虑“规范”排序关系:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
return lhs.a < rhs.a
|| ( !(rhs.a < lhs.a) && lsh.b < rhs.b )
|| ( !(rhs.a < lhs.a) && !(rhs.b < lhs.b) && lhs.c < rhs .c )
|| ...
}
};
显然,没有人会真正写出这样的东西,但它完全符合对所需内容的正式定义。
当然,如果我们可以将数据成员想象为一个数组,我们可以将其重写为一个循环,利用之前!(rhs[i-1] < lsh[i-1]
在每种情况下建立的优势:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
int i = 0;
while ( i != N && !(lhs[i] < rhs[i]) && !(rhs[i] < lhs[i]) ) {
++ i;
}
return i != N && lhs[i] < rhs[i];
}
};
或者,如果所有元素都是全序的,那么==
在它们上也定义了 ,我们可以假设它对应于弱偏序建立的等价关系:
struct Compare
{
bool operator()( C const& lhs, C const& rhs ) const
{
int i = 0;
while ( i != N && !(lhs[i] == rhs[i]) ) {
++ i;
}
return i != N && lhs[i] < rhs[i];
}
};
剩下的就是以某种方式将其转换为可以处理任意类型元素的任意顺序的东西。有句老话,每个问题的解决方案都是额外的间接级别,它适用于这里。
首先,我们需要一些方法来处理每个元素的不同类型。多态性似乎是合适的(尽管如果在编译时评估元素的顺序是固定的,则可以使模板工作):
struct CompareOneElementOfC
{
virtual bool isLessThan( C const& lhs, C const& rhs) const = 0;
virtual bool isEqual( C const& lhs, C const& rhs) const = 0;
};
template <typename T, T C::*ptr>
struct ConcreteCompareOneElementOfC : public CompareOneElementOfC
{
virtual bool isLessThan( C const& lhs, C const& rhs) const
{
return lhs.*ptr < rhs.*ptr;
}
virtual bool isEqual( C const& lhs, C const& rhs) const
{
return lhs.*ptr == rhs.*ptr;
}
};
根据元素的类型,您可能需要手写特定的具体实例。如果任何元素不支持总排序,则必须省略
isEqual
, 并相应地修改以下代码。
到目前为止,我们需要每个具体比较的一个静态实例:
ConcreteCompareOneElementOfC<int, &C::a> const c1;
ConcreteCompareOneElementOfC<double, &C::b> const c2;
// ...
最后,将这些实例的地址放在一个表中:
CompareOneElementOfC const* const cmp[] = { &c1, &c2 ... };
您可以为不同的订单设置不同的表。如果只有几个,为每个定义静态表,并完成它。如果排序可以是任意的,请在每次排序之前以所需的顺序动态创建表。
最后:
class Compare
{
CompareOneElementOfC const* const* begin;
CompareOneElementOfC const* const* end;
public:
template< size_t N >
Compare( CompareOneElementOfC const* const (&cmp)[N] )
: begin( cmp )
, end( cmp + N )
{
}
bool
operator()( C const& lhs, C const& rhs ) const
{
auto current = begin;
while ( current != end && (*current)->isEqual( lhs, rhs ) ) {
++ current;
}
return current != end && (*current)->isLessThan( lhs, rhs );
}
}
(请注意,我还没有实际测试过这段代码,所以可能存在拼写错误和其他错误。不过,基本思想应该在那里。)