对象集的排序不正确
Sorting of set of objects is incorrect
当我打印整套时,结果未排序并且包含一个重复项。
对象 Person
有姓氏、姓氏和出生年份(这 3 个都是字符串)。我首先按出生年份排序,然后按姓氏排序。本质上,没有相同的人(但即使是这样,也应该在将它们插入 set
时将其删除)。
更具体地说,我创建了一组这样的人:
std::set <Person> greatUncles;
并像这样插入它们:
greatUncles.insert(Person("bla", "bla", "1900"));
以下是 class 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
我一辈子都弄不明白我做错了什么。
您正在将 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::tie
d 一起进行比较手动实施它们一样。
std::set
要求比较器提供严格的弱排序。其中一部分是 if a < b == true
then b < a == false
但你没有这个。假设出生年份和姓氏相同,只有姓氏不同。在您的示例中,您将 return 一些正数或负数转换为 true
,因为只有 0
是 false
。如果你 运行 向后检查,那么你会得到相反的整数值,但它仍然会导致 true
.
为了解决这个问题,C++11 提供了 std::tie
,您可以使用它来构建成员的 std::tuple
,并且它的 operator <
是为了做正确的事情而构建的。这让你的代码看起来像
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
的运算符 ==、!=、<、<=、> 和 >=,只要您希望比较所有成员,它们就会 "do the right thing"按照它们在 class.
中定义的顺序
当我打印整套时,结果未排序并且包含一个重复项。
对象 Person
有姓氏、姓氏和出生年份(这 3 个都是字符串)。我首先按出生年份排序,然后按姓氏排序。本质上,没有相同的人(但即使是这样,也应该在将它们插入 set
时将其删除)。
更具体地说,我创建了一组这样的人:
std::set <Person> greatUncles;
并像这样插入它们:
greatUncles.insert(Person("bla", "bla", "1900"));
以下是 class 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
我一辈子都弄不明白我做错了什么。
您正在将 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::tie
d 一起进行比较手动实施它们一样。
std::set
要求比较器提供严格的弱排序。其中一部分是 if a < b == true
then b < a == false
但你没有这个。假设出生年份和姓氏相同,只有姓氏不同。在您的示例中,您将 return 一些正数或负数转换为 true
,因为只有 0
是 false
。如果你 运行 向后检查,那么你会得到相反的整数值,但它仍然会导致 true
.
为了解决这个问题,C++11 提供了 std::tie
,您可以使用它来构建成员的 std::tuple
,并且它的 operator <
是为了做正确的事情而构建的。这让你的代码看起来像
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
的运算符 ==、!=、<、<=、> 和 >=,只要您希望比较所有成员,它们就会 "do the right thing"按照它们在 class.