在 C++ 中重载比较运算符会导致“无效运算符<”

Posted

技术标签:

【中文标题】在 C++ 中重载比较运算符会导致“无效运算符<”【英文标题】:Overloading comparision operator in C++ results in "invalid operator<" 【发布时间】:2011-08-12 18:41:57 【问题描述】:

目前正在尝试在 C++ 中对对象向量进行排序,每个对象都包含一个字符串

字符串可以包含字母或数字(由于设计限制,这是必要的,因为比较器可以更改)。

此刻,对象的类被重载了,所以当比较两个对象时,比较它们包含的字符串。这在一定程度上有效——但是,当我使用排序操作(例如 STL 排序)将对象按顺序排列时,它将按顺序对三个字符串进行排序,例如“1”、“4”、“12” “1”、“12”、“4”。 4 大于 12,但因为它从最左边的数字开始比较,所以会发生这种“不正确”的排序。

我最初的反应是改变我重载比较操作的方式。我会首先检查我正在比较的字符串的长度——如果字符串的内容更大或更小,这将是一个迹象。

// overloaded comparision operators
friend bool operator<(const nodeRecord & record1, const nodeRecord & record2)
    // we need to deal with strings of different lengths...
    if(record1.comparator.length() < record2.comparator.length())
        return true;
    else
        return (record1.comparator < record2.comparator);

此操作在运行时导致“表达式:无效运算符

关于我在哪里犯错的任何想法?看来我应该能够准确地指示操作我希望排序操作如何发生——即使它是无效的,因为我目前正在使用一个向量来包含对象。

nodeRecord对象初始化期间的比较器:

nodeRecord(int fromNode, int toNode, int connectionCost, bool compareByCost = false)
    // take the provided stock information and insert it into the object
    stringstream fromNodeSS;
    fromNodeSS << fromNode;
    this->fromNode = fromNodeSS.str();
    stringstream toNodeSS;
    toNodeSS << toNode;
    this->toNode = toNodeSS.str();
    this->connectionCost = connectionCost;

    // set the comparator to our chosen comparision term
    if (!compareByCost)
        this->comparator = this->fromNode; // we use from node in this case, since we build the tree outwards
    
    else
        stringstream ss;
        ss << this->connectionCost;
        this->comparator = ss.str(); // we use the connection cost in this case, to allow us to sort new connections
    

    // set this as a non-null (active) record
    this->nullRecord = false;

【问题讨论】:

什么是比较器?发布代码。 你能说明比较器的定义吗? @Mike 和 @Mario -- 比较器在 nodeRecord 对象的初始化期间被初始化。你可以在上面看到这个。 【参考方案1】:

您的操作员实际上是无效的。

如果您希望运算符&lt; 可用于排序,它必须具有许多数学属性。一个是 AntiSymmetry 属性:

x &lt; y =&gt; !(y &lt; x)

让我们定义x = "b"y = "aa"

x &lt; y 因为"b" 的长度低于"aa" 的长度 y &lt; x 因为"aa" 不如"b"

嗯?

另请注意,如果数字以0s 为前缀,您的定义会很奇怪。

哦,比较字符串比比较数字要慢。

我的看法?停止使用比较信息更改节点。实际的比较模式与节点本身无关。

那你就写两种比较方法,一种按成本比较,另一种按产地比较。


回到原来的问题,如何编写一个考虑["a", "b", "aa"]排序的比较器?

您快到了,但“长度”比较不完整。只有在长度不同的情况下,您才需要回退到实际的词法比较,因此您忘记了右侧参数的长度低于左侧参数的情况。

因此正确的形式是,假设有两个字符串:

bool compare(std::string const& lhs, std::string const& rhs) 
  if (lhs.length() < rhs.length())  return true; 
  if (rhs.length() < lhs.length())  return false;  // don't forget this
  return lhs < rhs;

【讨论】:

谢谢!您的解决方案确实帮助我发现并理解了问题所在,并提醒了我另一个我忘记的案例。【参考方案2】:

找到下面抛出错误的代码段,然后想想我的重载操作是如何工作的。

template<class _Ty1, class _Ty2> inline
    bool _Debug_lt(_Ty1& _Left, _Ty2& _Right,
        _Dbfile_t _File, _Dbline_t _Line)
       // test if _Left < _Right and operator< is strict weak ordering
    if (!(_Left < _Right))
        return (false);
    else if (_Right < _Left)
        _DEBUG_ERROR2("invalid operator<", _File, _Line);
    return (true);
    

工作解决方案是这样的(感谢 Matthieu M. 留下的 cmets 再次修改)

// overloaded comparision operators
friend bool operator<(const nodeRecord & record1, const nodeRecord & record2)
    // we need to deal with strings of different lengths...
    if(record1.comparator.length() > record2.comparator.length()
        && (record1.comparator.length() !=0 && record2.comparator.length() != 0))
        return false;
    else if(record1.comparator.length() < record2.comparator.length()
        && (record1.comparator.length() !=0 && record2.comparator.length() != 0))
        return true;
    else
        return (record1.comparator < record2.comparator);

感谢所有帮助过的人!

【讨论】:

【参考方案3】:

您为什么不使用单个比较器并让该功能更智能一些?让它检查开头的数字字符,如果是,则执行一对strtol()atoi() 并比较结果。

否则根据您的非数字要求比较字符串和字符的长度。

【讨论】:

我希望能够对字符串进行排序,就像我对数字进行排序一样。例如,刺“a”、“aa”和“b”应该按“a”、“b”、“aa”排序。我发布的方法是我所知道的唯一可以让我完成此任务的方法。 不幸的是,这不能解决我的问题。会出现同样的错误=( @BSchlinker:我的回答假设只使用一个 operator&lt; 函数,尽管它会比你的复杂得多。

以上是关于在 C++ 中重载比较运算符会导致“无效运算符<”的主要内容,如果未能解决你的问题,请参考以下文章

[ C++ ] C++类与对象(中) 类中6个默认成员函数 -- 运算符重载

C++ 重载 == 比较来自不同类的对象

C++ 继承多态关系中的赋值运算符的重载=operator()

C++ 继承多态关系中的赋值运算符的重载=operator()

多态性——运算符重载

C++如何重载指针的比较符