运营商LT;比较多个字段

时间:2012-07-03 13:52:26

标签: c++ comparison-operators lexicographic

我有以下运算符<应该首先按值排序,然后按另一个值排序:

    inline bool operator < (const obj& a, const obj& b) 
    {
        if(a.field1< b.field1)
            return true;
        else
            return a.field2 < b.field2;
    }

我觉得这是不正确的,如果没有对成员变量进行另一次第三次比较测试,你就不能这样做,但我找不到任何不起作用的例子。 那么这真的和预期的一样吗? 感谢

编辑: 我会把它编码为:

    inline bool operator < (const obj& a, const obj& b) 
    {
        if(a.field1< b.field1)
            return true;
                    else if(a.field1> b.field1)
            return false;
        else
            return a.field2 < b.field2;
    }

有什么不同吗?我问,因为我知道我的经验是正确的,但也比第一次更长

6 个答案:

答案 0 :(得分:32)

我想自己做这一切......

如果Obj::field2的值相等,则只应比较Obj::field1的值。

易于理解的方式:

/* This will meet the requirements of Strict-Weak-Ordering */

if (a.field1 != b.field1) return a.field1 < b.field1;
else                      return a.field2 < b.field2;

正确(推荐)方式:

正确的”实现方法仅使用operator<来比较字段,下面看起来比实际情况复杂。

然而,它会产生与之前编写的易于理解的示例相同的结果。

return a.field1 < b.field1 || (
  !(b.field1 < a.field1) && a.field2 < b.field2
);

必须有一种方法来实现operator<而不会引起很多麻烦?

C ++ 11

您可以使用已经std::tuple STL 中的operator<来定义多个字段,例如以下示例。

#include <utility>

...

inline bool
operator< (Obj const& lhs, Obj const& rhs)
{
  return std::tie (lhs.field1, lhs.field2) < std::tie (rhs.field1, rhs.field);
}

C ++ 03

如果你的编译器还没有对C ++ 11的支持,你只需要比较每个对象的两个字段,你就可以使用std::pair

std::make_pair的原因与上一个使用std::tie的示例相同。

#include <utility>

...

inline bool
operator< (Obj const& lhs, Obj const& rhs)
{
  return std::make_pair (lhs.field1, lhs.field2)
       < std::make_pair (rhs.field1, rhs.field2);
}

使用std::pair将需要创建成员的副本,这在某些情况下是不合需要的。

这是真的推荐的做法吗?

有关详细信息,请参阅以下问题/答案,但总结一下; c ++ 11方法不会产生太多开销,而且实现起来非常简单。

答案 1 :(得分:7)

想想如果a.field1大于b.field1a.field2小于b.field2会发生什么。在这种情况下,您在field2上比较基于 ,这不是您想要的。

你只想让field2field1字段相等的地方发挥作用,所以你要找的是(伪代码):

if a.field1 < b.field1: return true
if a.field1 > b.field1: return false
# field1s is equal here.
return a.field2 < b.field2

答案 2 :(得分:4)

没有。您还需要抓住(a.field1 > b.field1)

这不是一个严格的弱排序,因为它会给(1,2) < (2,1),但(2,1) < (1,2)

答案 3 :(得分:2)

这是一个依赖于逻辑短路规则的版本,以避免显式分支

template<typename T>
bool operator< (T const& a, T const& b)
{
        return (
                 ( a.field1 < b.field1 ) || (( a.field1 == b.field1 ) &&
                 ( a.field2 < b.field2 ))
        );
}

这假设您的原始类型field1具有operator==。为超过2个字段键入此内容会变得很繁琐,但如果您的课程std::lexicographical_compare将字段存储在obj内的某些类型std::array<T, N>和字段{{}},则可以使用T {1}}

N

请注意,有一份草稿N3326讨论了为类类型自动添加运算符template<typename T, int N> struct obj { std::array<T, N> field; }; bool operator< (obj const& a, T const& b) { return std::lexicographical_compare( a.field.begin(), a.field.end(), b.field.begin(), b.field.end() ); } ==

答案 4 :(得分:0)

下面描述的我的方法涉及一些宏,但在许多情况下仍然有用。也许也可以使用内联函数来完成类似的操作。

#define CMP_LT2(a, b) ((a) < (b) ? (a) : (b))
#define CMP_GT2(a, b) ((a) > (b) ? (a) : (b))
#define CMP_LTE2(a, b) ((a) <= (b) ? (a) : (b))
#define CMP_GTE2(a, b) ((a) >= (b) ? (a) : (b))
#define CMP_EQ2(a, b) ((a) == (b))
#define CMP_NEQ2(a, b) ((a) != (b))
#define CMP_LT3(a, b, c) (CMP_EQ2(a, b) ? (c) : CMP_LT2(a, b))
#define CMP_GT3(a, b, c) (CMP_EQ2(a, b) ? (c) : CMP_GT2(a, b))
#define CMP_LTE3(a, b, c) (CMP_EQ2(a, b) ? (c) : CMP_LT2(a, b))
#define CMP_GTE3(a, b, c) (CMP_EQ2(a, b) ? (c) : CMP_GT2(a, b))
#define CMP_EQ3(a, b, c) ((a) == (b) ? (c) : false)
#define CMP_NEQ3(a, b, c) ((a) != (b) ? true : (c))

然后假设您拥有:

struct Point3D {
    double x;
    double y;
    double z;
};

然后您写:

struct Point3D {
    double x;
    double y;
    double z;

    bool operator<(const Point3D& other) const noexcept
    {
        return CMP_LT3(z, other.z,
               CMP_LT3(y, other.y,
               CMP_LT2(x, other.x)));
    }
};

答案 5 :(得分:0)

您可以在c ++ 11或更高版本中使用可变参数模板

template<typename T>
bool less_than( const T& a, const T& b )
{
    return a < b;
}

template<typename T, typename... Args>
bool less_than( const T& a, const T& b, Args... args )
(
    if ( a < b )
          return true;
    else if ( b < a )
          return false;
    else
          return less_than(  args...  );
)

那你叫

return less_than(a.x,b.x,
                 a.y,b.y,
                 a.z,b.z);

只要type具有<运算符,它就支持任意数量的字段或类型。您可以混合类型。