23

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

    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;
    }

有什么区别吗?我问是因为我知道我的经验是正确的,但也比第一个长

4

6 回答 6

42

我愿意自己做这一切。。

您应该只比较 的值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从已定义多个字段的STLoperator<中使用,如下例所示。

#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);
}

usingstd::pair将需要创建成员的副本,这在某些情况下是不可取的。

这真的是推荐的做法吗?

请参阅以下问题/答案以获取更多信息,但要总结一下;c++11 方法不会造成太多开销,而且实现起来非常简单。

于 2012-07-03T13:55:08.817 回答
8

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

您只想field2在字段相等的情况下发挥作用field1,所以您要寻找的是(伪代码):

if a.field1 < b.field1: return true
if a.field1 > b.field1: return false
# field1s is equal here.
return a.field2 < b.field2
于 2012-07-03T13:56:30.437 回答
4

不,您还需要 catch (a.field1 > b.field1)

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

于 2012-07-03T13:54:29.807 回答
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和大小的字段中,则可以使用N

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()
        );
}

请注意,有一篇草稿N3326讨论了为类类型添加运算符==和自动。<

于 2012-07-03T14:41:55.010 回答
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)));
    }
};
于 2019-03-17T19:20:59.997 回答
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);

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

于 2019-10-11T17:46:27.003 回答