1

考虑以下结构:

struct Foo {
  const Bar* x;
  const Bar* y;

  Foo(const Bar* _x, const Bar* _y = nullptr) : x(_x), y(_y) { assert(x); }
}

我们如何在 x 和 y 上定义严格的弱排序,以便对象可以在 std::set 中使用?请注意,y 可以是空指针。如将 std::less 与 nullptr 一起使用中所述,std::less 在空指针上的行为未定义未指定。以下解决方案是否足够?

bool operator<(const Foo& rhs) const {
  uintptr_t numX = reinterpret_cast<uintptr_t>(x);
  uintptr_t numY = reinterpret_cast<uintptr_t>(y);

  uintptr_t numRhsX = reinterpret_cast<uintptr_t>(rhs.x);
  uintptr_t numRhsY = reinterpret_cast<uintptr_t>(rhs.y);

  return std::tie(numX, numY) < std::tie(numRhsX, numRhsY);
}

编辑:如果不是正确的方法是什么(例如如何将 std::less 与 std::tie 结合)?

4

1 回答 1

3

使用std::less<Bar*>就足够了(但使用operator<不是)。的指针特化std::less(正如对“将 std::less 与 nullptr 一起使用”的公认答案指出的那样)保证了总排序。与未指定比较nullptr意味着标准不强加特定的排序,但std::less仍必须产生排序(并且对于给定的指针pp < nullptr每次都必须产生相同的值)。

由于总排序比弱排序强,std::less因此在您的情况下使用就足够了。

编辑:如果不是正确的方法是什么(例如如何将 std::less 与 std::tie 结合)?

不幸的是,没有简洁的方法。由于std::tie返回一个std::tuple, 并且元组的比较是根据operator<它们的值(而不是std::less)定义的,所以你不能std::tie在这里真正使用。要使用std::less,您必须手动进行:

bool operator<(const Foo& rhs) const {
  if (std::less<>{}(x, rhs.x))
    return true;
  if (std::less<>{}(rhs.x, x))
    return false;
  return std::less<>{}(y, rhs.y);
}

顺便说一句,您当前的实现(将指针重新解释为整数)也会产生总排序(显然,因为您正在比较整数),但您将拥有实现定义的行为(来自),而不是未指定的行为。reinterpret_cast

于 2019-01-15T15:36:44.463 回答