【问题标题】:std::set use of operator<std::set 使用操作符<
【发布时间】:2019-02-07 20:35:25
【问题描述】:

这个问题与我在这里的另一个问题有点相关:link(参见 cmets 中的讨论)。基本上,我遇到了以下问题:

我有一堂课node。其中有一些字段,最重要的是:GHpospos 是 Qt 的 QPoint,但为了示例,我已经将其重写为我自己的类 Point。请看下面的例子:

#include <algorithm>
#include <iostream>
#include <memory>
#include <set>

class Point
{
public:
    int _x, _y;
    Point() : _x(0), _y(0) {}
    Point(int x, int y) : _x(x), _y(y) {}
    bool operator==(const Point& p) const { return _x == p._x && _y == p._y; }
    bool operator!=(const Point& p) const { return _x != p._x && _y != p._y; }
};

class node
{
public:
    node() {}
    node(const Point& p) : pos(p) {}
    bool operator==(const node& o) const { return pos == o.pos; }
    bool operator==(const Point& o) const { return pos == o; }
    bool operator!=(const node& o) const { return pos != o.pos; }
    bool operator<(const node& o) const { return G + H < o.G + o.H; }
    Point pos;
    std::shared_ptr<node> parent;
    int G = 0;
    int H = 0;
};

int main()
{
    node n1(Point(6, 7));
    n1.G = 1;
    n1.H = 1;
    node n2(Point(1, 1));
    n2.G = 2;
    n2.H = 2;
    node n3(Point(2, 2));
    n3.G = 1;
    n3.H = 1;
    std::set<node> nodes;
    nodes.insert(n1);
    nodes.insert(n2);
    nodes.insert(n3);
    auto min = (*std::min_element(nodes.begin(), nodes.end())).pos;
    std::cout << min._x << " " << min._y << '\n';
    std::cout << nodes.size() << '\n';
}

这个程序的输出是:

>main.exe
6 7
2

所以搜索最低元素是成功的(使用operator&lt;)。所以这就是我想要的。但正如你所看到的,我创建的三个nodes 有不同的.pos 字段(基本上是坐标)。所以我希望他们所有人都出现在一个集合中。换句话说,你可以说每个节点的“唯一性”应该由.pos字段决定(实际上是使用这个字段的operator==,我上面定义的)。其中一个节点被认为不是唯一的,因为std::set 使用operator&lt; 比较其元素。所以n1n3 具有相同的G+H 值并且它们被认为是相等的(输出第二行中的2 是集合元素的数量-> 2,而不是3)。

在我知道std::set 使用operator&lt; 来比较相等之前,我已经写了operator==operator!=,认为该集合将使用其中一个来比较我班级的对象。但它使用operator&lt;

所以我的问题是为什么它实际上使用这个运算符。使用operator==operator!=不是更方便吗?

对我来说,这有点让工作复杂化,因为我必须想出另一种方法来编写 operator&lt; 或使用不同的容器(因此编写 lambda),或者我可以使用 .pos 比较 operator&lt; 并重写 @ 987654352@本人(取GH在账户中的总和,而不是.pos字段)

【问题讨论】:

  • std::set 实现为二叉树 - 这要求您能够比较小于关系的条目。
  • 哦,这可以解释很多。实际上不知道为什么我的问题被某人否决了,但仍然,谢谢!
  • 顺便说一句:您的operator!= 应该使用|| 而不是&amp;&amp;
  • 严格来说std::set是如何实现的并不重要,集合中的元素是排序的,你不能只使用==!=对元素进行排序,你需要&lt;
  • 所以在这种情况下,我必须手动搜索最低元素或相等检查(检查元素是否已存在于容器中):(

标签: c++ stl set operator-overloading


【解决方案1】:

您尝试实现的目标违反了std::setStrict Weak Ordering 要求。基本上,如果您有 2 个数字,并且两者都不小于另一个,那么它们一定是相同的!它们也不能不同(使用不同的语法检查时)。

您的所有比较运算符都应该一致地定义,以便您的类型有一个清晰的概念。您的类型中的哪些成员显着,即对价值有贡献?可能还有其他成员,但绝不应在比较运算符中检查它们。

一个例子是std::vector。如果两个vectors 都包含a, b, c,那么它们是相等的。它们可能有不同数量的未使用存储空间 (vector.capacity()),但这不是任一对象的 的一部分。

如果你有时间,John Lakos has presented about thisAlexander Stepanov has written about it

【讨论】:

  • 问题是,GH之和应该用于比较“哪个节点少”(A*算法中的F值)和.pos字段(节点坐标)应该用于区分一个节点和另一个
  • 我明白你想做什么。我试图告诉您这是一个坏主意,尤其是与您尝试使用的容器不一致。对于节点的“值”,您有两个不同的概念。我认为您可以通过在您的operator&lt; 中考虑.pos 来实现您想要的,但无论您做什么,请确保&lt;==(以及所有其他人)彼此同意!
  • 谢谢,我会这样做的。我只是很困惑为什么 std::set 会使用 operator&lt; 进行相等性或唯一性检查,但现在当我知道它基于二叉树时 - 一切似乎都清楚了!但是我仍然不知道我违反了哪条 SO 规则以至于我被否决了 :( 我想我已经清楚地表达了我的问题,提供了一个完整的例子,写的可能不是 100% 完美,但我猜是可以理解的英语,我不知道。我想我应该再读一遍规则
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
  • 2013-11-23
  • 2014-04-15
  • 2015-10-11
  • 1970-01-01
相关资源
最近更新 更多