在C++中重载比较运算符会导致“invalid operator <”错误

7

目前尝试在C++中对包含字符串的对象向量进行排序。

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

目前,对象的类已经过载,因此当比较两个对象时,它们包含的字符串会进行比较。这在一定程度上有效-然而,当我使用排序操作(例如STL sort)将对象排序时,它会将三个字符串排序为“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 Bailey
请问您能展示一下比较器的定义吗? - Mario The Spoon
@Mike 和 @Mario -- 比较器在 nodeRecord 对象初始化时被初始化。你可以在上面看到这一点。 - BSchlinker
3个回答

10

你的运算符无效。

如果要将运算符用于排序,则必须具有一些数学特性,其中之一是反对称性质:

x < y => !(y < x)

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

  • x < y,因为 "b" 的长度小于 "aa" 的长度
  • y < x,因为 "aa" 小于 "b"

嗯?

还要注意,如果数字以 0 为前缀,则您的定义会变得奇怪。

哦,而且比较字符串比比较数字慢得多。

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

然后,您只需编写两个比较方法,一个按成本比较,另一个按原点比较。


回到最初的问题,如何编写一个比较器来考虑 ["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;
}

谢谢!你的解决方案帮助我找到并理解了问题所在,并提醒了我另一个我已经忘记的情况。 - BSchlinker

1
发现以下代码段引发了错误,然后思考我的重载操作是如何工作的。
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.留下的评论)
// 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);
}

感谢所有帮助过我的人!


0
为什么不使用单个比较器并使该函数更加智能?让它在开头检查数字字符,如果是,则执行一对strtol()atoi()并比较结果。
否则,根据您的非数字要求比较字符串的长度和字符。

我想要能够像排序数字一样排序字符串。例如,字符串 "a"、"aa" 和 "b" 应该按照 "a"、"b"、"aa" 的顺序排序。我发布的方法是我所知道的唯一可以实现这一点的方法。 - BSchlinker
不幸的是,这并不能解决我的问题。相同的错误仍会发生 =( - BSchlinker
@BSchlinker:我的回答假设只使用一个operator<函数,尽管它比你的要复杂得多。 - wallyk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接