【问题标题】:strict weak ordering confusion严格的弱序混淆
【发布时间】:2012-11-14 13:14:06
【问题描述】:

我对严格的弱排序以及在定义运算符时如何使用它感到困惑

struct Plane
{
    std::string name;

    int xrudder;
    int yrudder;

    int wingwidgets;

    bool hasLegacyEngine;
};


struct Airport
{
    bool securityCheck;
    unsigned __int64 capacity;

    std::vector<Plane> planes;
};

我想创建一个std::set 的机场。我需要定义 operator

struct cmpless
{
bool operator()(const Airport& left, const Airport& right)
    {
        //?
    }
}; 

std::set<Airport, cmpless> airportSet;

一个机场“小于”另一个机场是没有意义的。只有当机场的统计数据相等时才有意义。

我如何确定我对 operatoroperator<?

如果可能的话,一个带有解释的例子会很棒!

【问题讨论】:

  • 我看到你给每架飞机起了名字,但你的机场没有名字。如果你给机场一个名字,你可以使用词法字符串比较,因为它定义了一个严格的弱排序。

标签: c++ operator-overloading set strict-weak-ordering


【解决方案1】:

musingstudio blog 上找到了一个不错的解决方案,并认为我在这里分享它以供下一个需要的人使用(即使 Dietmar 可能正确地认为地图不合适):

bool operator <(const T& rhs) const
{
  if (a < rhs.a)
    return true;
  else if (rhs.a < a)
    return false;

  if (b < rhs.b)
    return true;
  else if (rhs.b < b)
    return false;

  // repeat for all child elements c, d, e etc
  return false;
}

【讨论】:

    【解决方案2】:

    如果一个Airport 出现在另一个Airport 之前“没有意义”,那么使用std::set&lt;Airport&gt; 也没有意义。此容器利用订单金额元素在O(log(n)) 操作中定位对象(其中n 是容器的大小)。如果您只能通过身份识别对象,那么您可以实现的最佳复杂度是O(n)。您可以使用std::find()std::find_if() 和序列容器之一的组合,例如std::vector&lt;Airport&gt;std::deque&lt;Airport&gt;

    由于您不需要根据operator&lt;() 定义顺序,因此将Airports 放入某种顺序可能是合理的,以便将它们定位在std::set&lt;Airport&gt; 中,由使用与std::less&lt;Airport&gt; 不同的比较函数对象。不过,您当前在 Airport 对象中拥有的属性看起来并不像合适的键。事实上,它们看起来都好像是可变的,也就是说,你可能不想要std::set&lt;Airport&gt;,因为你不能修改std::set&lt;T&gt; 中的元素(好吧,至少,你不应该;是的,我知道你可以用mutable 来耍花招,但这势必会破坏元素的顺序)。

    基于此,我建议使用std::map&lt;std:string, Airport&gt;std::string 用于识别机场,例如,使用机场代码,如纽约约翰肯尼迪机场的"JFK""LHR" 伦敦希思罗机场。方便的是,已经在字符串上定义了严格的弱顺序。

    也就是说,要在一组对象 O 上定义一个 strict weak order,您需要一个二元关系 r(x, y),使得以下条件适用于元素 xyz来自O

    • 不自反:r(x, x) == false
    • 不对称:r(x, y) == true 暗示r(y, x) == false
    • 传递:r(x, y) == truer(y, z) == true 暗示 r(x, z) == true
    • 不可比性:r(x, y) == falser(y, x) == falser(y, z) == falser(z, y) == false 暗示 r(x, z) == falser(z, x) == false

    前三个应该足够简单。最后一个起初有点奇怪,但实际上也没有那么难:基本思想是关系并不完全对元素进行排序,而是将它们分组到等价的类中。如果您认为r 的关系“小于”它只是说如果x 既不小于y 也不小于y 小于x,那么xy 是相等的。无与伦比的元素只是等价的。

    标准容器使用严格的弱顺序,但例如 std::set&lt;T&gt;std::map&lt;K, V&gt; 只保留一个版本的等效键。很好,这已经足够了,但通常只使用一个严格的弱顺序的总顺序更简单,其中每对元素 xy 要么是 r(x, y) == true 要么 r(y, x) == true (但是,由于不对称不是两者都有)。

    【讨论】:

    • '如果你只能通过身份来识别对象,你能达到的最佳复杂度是 O(n)',实际上,你可以使用基于哈希的容器做得更好
    • 如果您的测试是is_identical(x, y),哈希对您有何帮助?陈述相当精确:您可以询问两个对象是否相同,而不是更多而不是更少。如果还有其他属性,您可能会做得更好。鉴于属性似乎是可变的,这似乎是最好的,因为任何哈希值都会改变。
    • 你说得对,我想的更多的是如果你不能订购,你能做些什么,而不是你能做的就是测试身份。
    • 第一段末尾有一些模棱两可/误导性的文字:“如果您只能通过身份识别对象,那么您可以实现的最佳复杂度是 O(n)。”鉴于这个问题的上下文,我将“如果你只能通过身份识别一个对象”解释为“如果你有 == 但没有 operator或编写一个哈希函数(这几乎总是可能的),在这种情况下,你可以实现的最佳复杂度实际上是 O(1)(更好比 std::set!),通过使用哈希映射(例如 std::unordered_set)。
    • 拥有“一组”机场非常有意义。只是不是像sdt::set 这样的有序集合。如果需要根据它们的属性唯一标识它们,您应该使用std:unordered_set。这只需要一个散列函数,并且相等运算符对于像机场这样的对象是完全有效的。
    【解决方案3】:

    如果每个成员都定义了&lt;,您可以执行类似于字典顺序的操作:

       struct cmpless
        {
        bool operator()(const Airport& left, const Airport& right)
            {
                return
                  left.securityCheck < right.securityCheck
                  || ((left.securityCheck == right.securityCheck
                       && left.capacity < right.capacity)
                      || (left.capacity == right.capacity
                          && left.planes < right.planes));
            }
        }; 
    

    【讨论】:

    • 我可能弄错了——解析布尔表达式不是我的强项——但我认为你的括号应该是:return left.securityCheck &lt; right.securityCheck || (left.securityCheck == right.securityCheck &amp;&amp; (left.capacity &lt; right.capacity || (left.capacity == right.capacity &amp;&amp; left.planes &lt; right.planes)));
    • 我认为 dhavenith 可能对括号是正确的,但我不确定。您能否根据他的建议仔细检查您的答案并告诉我您的想法?
    猜你喜欢
    • 1970-01-01
    • 2013-02-14
    • 2010-11-20
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2019-06-09
    • 2016-02-04
    • 2010-11-02
    相关资源
    最近更新 更多