C++ 对象集的排序不正确

C++ 对象集的排序不正确,c++,c++11,object,set,overloading,C++,C++11,Object,Set,Overloading,当我打印整个集合时,结果是未排序的,并且它包含一个副本。 对象Person有姓氏、家庭名和出生年份(三个都是字符串)。我先按出生年份排序,然后按家庭名称,再按姓氏排序。就其本身而言,不存在完全相同的人(但即使是这样,在将他们插入集合时也应将其删除) 更具体地说,我创建了一组这样的人: std::set <Person> greatUncles; greatUncles.insert(Person("bla", "bla", "1900")); Archibald Furtweg

当我打印整个集合时,结果是未排序的,并且它包含一个副本。 对象
Person
有姓氏、家庭名和出生年份(三个都是字符串)。我先按出生年份排序,然后按家庭名称,再按姓氏排序。就其本身而言,不存在完全相同的人(但即使是这样,在将他们插入
集合时也应将其删除)

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

std::set <Person> greatUncles; 
greatUncles.insert(Person("bla", "bla", "1900"));
Archibald Furtweger 1967
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967
以下是课堂上的基本内容
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;
}
但看起来是这样的:

std::set <Person> greatUncles; 
greatUncles.insert(Person("bla", "bla", "1900"));
Archibald Furtweger 1967
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967

我一辈子都想不出我做错了什么。

你是在把
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;
}

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

std::tie
d一起。

std::set
要求比较器提供严格的弱排序。部分原因是如果
a
那么
b
但是你没有这个。让我们想象一下,出生年份和姓氏是一样的,只是姓氏不同。在您的示例中,您将返回一些正数或负数,这些正数或负数转换为
true
,因为只有
0
false
。如果向后运行检查,则整数中会出现相反的值,但结果仍然是
true


要修复此问题,C++11提供了
std::tie
,可用于构建成员及其
运算符的
std::tuple
。请不要将
放在代码段中,或将其放在注释中,这会使您的代码无法直接复制/粘贴/运行。感谢您的反馈,我会记住的。我会认为这是一个惯用的解决方案。1@JesperJuhl至少现在是这样。C++20允许使用do
auto操作符(const class\u name&)const=default当然,但这仍然是一个未发布的标准。您发布的是C++17及更低版本的惯用解决方案。:)将方法
编写为_tuple
可能会避免重复
p1
p2
的成员@Jarod42如果tie创建了一个引用元组,而不是
到_tuple()
来创建一个类似元组的副本,那么将其命名为
会更好吗?我这样问是因为我根本没有理由这么做。
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());
}
auto operator<=>(const Person&) const = default;