如果我有一个类,我希望能够排序(即支持小于概念),并且它有几个数据项,因此我需要进行字典顺序排序,那么我需要这样的东西:
struct MyData {
string surname;
string forename;
bool operator<(const MyData& other) const {
return surname < other.surname || (surname==other.surname && forename < other.forename); }
};
对于任何具有超过 2 个数据成员的事物来说,这变得非常难以管理。 有没有更简单的方法来实现它?数据成员可以是任何 Comparable 类。
随着 C++11 的出现,有一种新的、简洁的方法可以使用 std::tie 来实现此目的:
bool operator<(const MyData& other) const {
return std::tie(surname, forename) < std::tie(other.surname, other.forename);
}
tuple
是一个好主意,但如果你想继续为你的成员变量命名,那么像这样重构你的比较函数可能就足够了:
struct MyData {
string surname;
string forename;
string var;
// ...
bool operator<(const MyData& other) const {
if (surname != other.surname) return surname < other.surname;
if (forename != other.forename) return forename < other.forename;
if (var != other.var) return var < other.var;
// ...
return false; //< They are equal
}
};
根据您的口味,您甚至可能需要像
#define COMPARE(field) if (field != other.field) return field < other.field;
这样的宏来减少重复。然后该函数将变成一个 COMPARE
调用列表。
您可以将数据存储在
boost::tuple
中,它提供字典比较,并提供命名访问器函数,类似于:
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
struct Data {
string &surname() {return stuff.get<0>();}
string &forename() {return stuff.get<1>();}
// it would be polite to add const overloads too.
bool operator<(const Data &other) const {return stuff < other.stuff;}
private:
boost::tuple<string, string> stuff;
};
我相信这也可以作为
std::tr1::tuple
提供,并且在即将推出的标准中将是 std::tuple
。
维护访问器列表可能比维护比较代码更易于管理。
如果所有成员都有相同的类型,您可以将它们放入
std::vector
。默认情况下 std::lexicographical_compare
将用于比较向量。
这里是与上面 Magnus Hoff 的答案相同的变体,但它使用参数数量可变的模板,并且适用于相同类型的任何对序列:
template<typename A, typename ... Args>
inline bool lt_lex(const A & a1, const A & a2, Args ... args)
{
if constexpr (sizeof...(args) == 0)
{
return a1 < a2;
}
else
{
return (a1 == a2) ? cmp::lt_lex(args...) : (a1<a2);
}
}
struct MyData{
int a;
char b;
std::<string> c;
bool operator<(const MyData& x) const
{
return lt_lex(a, x.a, b, x.b, c, x.c);
}
}
但是,请注意,上面的代码和建议的答案实际上进行了两次比较: 比如说
lt_lex(a1,a2, b1,b2)
减少到
if(a1==a2) // one comparison of a1 and a2
{
return b1<b2;
}
else
{
return a1<a2; // repeating comparison of a1 and a2
}
也许编译器可以对此进行优化,但这应该针对每种具体情况进行调查。
如果
可以通过计算和存储差异来改进代码
template<typename ... Args>
inline bool lt_lex(int & a1, int & a2, Args ... args)
{
if constexpr (sizeof...(args) == 0)
{
return a1 < a2;
}
else
{
int d = a2-a1; // compute the difference once
if (d==0) // check zero one bit
{
return cmp::lt_lex(args...);
}
else
{
return d>0; // check sign bit
};
}
}