【问题标题】:Sorting of set of objects is incorrect对象集的排序不正确
【发布时间】:2019-01-28 17:04:09
【问题描述】:

当我打印整套时,结果是未排序的并且它包含一个副本。 对象Person 有一个姓氏、一个姓氏和出生年份(这三个都是字符串)。我首先按出生年份排序,然后按姓氏,然后按姓氏。本质上,没有相同的人(但即使是这种情况,也应该在他们插入set 时将其删除)。

更具体地说,我创建了一组这样的人:

std::set <Person> greatUncles; 

并像这样插入它们:

greatUncles.insert(Person("bla", "bla", "1900"));

这是Person 类的基本内容:

class Person {
public:
  //...

  Person(std::string s, std::string f, std::string y)
    :surname(s), familyname(f), yearOfBirth(y)
  {
  }

  //...

  std::string getSurname() const {
    return surname;
  }

  std::string getFamilyname() const {
    return familyname;
  }

  std::string getYearOfBirth() const {
    return yearOfBirth;
  }

private:
  std::string surname;
  std::string familyname;
  std::string yearOfBirth;
};

//to print the set, overload the '<<' operator
std::ostream &operator<<(std::ostream &o, const Person &person) {
  o << person.getSurname() << " "
    << person.getFamilyname() << " "
    << person.getYearOfBirth() << std::endl;
  return o;
}

//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
  int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());

  if (compareYearOfBirth == 0) {
    int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
    if (compareFamilyname == 0) {
      return p1.getSurname().compare(p2.getSurname());
    } else
      return compareFamilyname;
  } else
    return compareYearOfBirth;
}

这是我如何打印叔叔的集合:

void printGreatUncles(std::set <Person> &greatUncles) {
    std::ofstream outputFile;
    outputFile.open("greatuncle.dat");

    if (outputFile.is_open()) {
      for(Person const & person:greatUncles) {
        outputFile << person;
      }
      outputFile.close();
    }
  }

现在某种情况下的输出应该是这样的(按年份排序):

Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967

但它看起来像这样:

Archibald Furtweger 1967
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967

我一生都无法弄清楚我做错了什么(事情)。

【问题讨论】:

  • 请不要把...放在你的代码sn-p中,或者把它们放在cmets中,这会使你的代码无法直接复制/粘贴/运行。
  • 感谢您的反馈,我会记住的。

标签: c++ c++11 object set overloading


【解决方案1】:

std::set 要求比较器提供严格的弱排序。其中一部分是如果a &lt; b == true 然后b &lt; a == false 但你没有这个。假设出生年份和姓氏相同,只有姓氏不同。在您的示例中,您将返回一些转换为true 的正数或负数,因为只有0 是false。如果您向后运行检查,那么您会在整数中得到相反的值,但它仍然会导致 true。

要修复这个 C++11 提供 std::tie,您可以使用它来构建成员的 std::tuple,并且它的 operator &lt; 是为了做正确的事情而构建的。这让你的代码看起来像

bool operator< (Person const &p1, Person const &p2) {
  return std::tie(p1.getYearOfBirth(), p1.getFamilyname(), p1.getSurname()) < 
         std::tie(p2.getYearOfBirth(), p2.getFamilyname(), p2.getSurname());
}

如果你想继续这样做并且可以使用 C++20,那么你可以添加到 Person

auto operator<=>(const Person&) const = default;

这将自动为您提供Person 的运算符 ==、!=、 和 >=,只要您希望按顺序比较所有成员,它们就会“做正确的事”它们是在类中定义的。

【讨论】:

  • 我认为这是惯用的解决方案。 +1
  • @JesperJuhl 至少现在是这样。 C++20 让我们使用 auto operator&lt;=&gt;(const class_name&amp;) const = default;
  • 当然,但这仍然是一个未发布的标准。您发布的是 C++17 及更低版本的惯用解决方案。 :)
  • 编写方法as_tuple 可以避免重复p1 和p2 的成员。
  • @Jarod42 将其命名为 as_tie() 会更好吗,因为 tie 创建了一个引用元组,而不是 to_tuple() 来创建一个类似元组的副本?我问是因为我根本没有理由这样做。
【解决方案2】:

您将std::string::compare 返回的int 作为bool 返回。这不是您想要的,因为 1 和 -1 都转换为 true。

正确的比较代码是:

//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
  int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());

  if (compareYearOfBirth == 0) {
    int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
    if (compareFamilyname == 0) {
      return p1.getSurname().compare(p2.getSurname()) < 0;
    } else
      return compareFamilyname < 0;
  } else
    return compareYearOfBirth < 0;
}

NathanOliver 的 std::tie 选项比上述选项更不容易出错(尽管您仍然可以轻松搞砸任何复制粘贴的内容 - 我以前做过)。

在 C++20 中会有一个更简单的解决方案(参见cppreference):

class Person {
  // ...
public:
  auto operator<=>(const Person &) const = default;
}

这将基本上提供所有比较,就像您通过比较所有成员 std::tied 一起手动实现它们一样。

【讨论】:

    猜你喜欢
    • 2014-04-21
    • 2021-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    相关资源
    最近更新 更多