【问题标题】:Bug in std::sort?std::sort 中的错误?
【发布时间】:2018-07-05 10:22:32
【问题描述】:

好的,我通常默认假设错误在我的代码中,但我所看到的对我来说毫无意义。


我有一个std::vector<DocumentWidget *>,我想通过一些相对简单的算法对其进行排序。这就是我所看到的。

代码如下所示:

std::vector<DocumentWidget *> documents = DocumentWidget::allDocuments();

// NOTE(eteran): on my system, I have observed a **consistent** crash
// when documents.size() == 17 while using std::sort.
std::sort(documents.begin(), documents.end(), [](const DocumentWidget *a, const DocumentWidget *b) {

    int rc = (a->filenameSet_ == b->filenameSet_) ? 0 : a->filenameSet_ && !b->filenameSet_ ? 1 : -1;
    if (rc != 0) {
        return rc < 0;
    }
    if(a->filename_ < b->filename_) {
        return true;
    }
    return a->path_ < b->path_;
});

看起来很简单,但是当我在列表中有第 17 项时它会崩溃!排序谓词显然不会以任何方式修改vector,所以我看不出这是一个问题。到目前为止,地址清理程序和 valgrind 没有显示任何错误。

qSort 不会崩溃,似乎工作正常。没有其他正在运行的线程接触到这些数据,无论我走多慢,它都会可靠地发生......所以这不是竞争条件。

当我在调试器中查看时,a 参数似乎是“结束后”迭代器所指向的位置。但如果 std::sort 的行为是 ,则不应该发生这种情况。

注意std::vector 中有 17 项,我强制调试器显示第 18 项以说明 a 似乎来自哪里。

我无法想象std::sort 被窃听了,但我真的很难找到另一种解释。我在这里遗漏了一些明显的错误吗?!

【问题讨论】:

  • if(a-&gt;filename_ &lt; b-&gt;filename_) { return true; } 应该可能与相反。您可以同时使用a &lt; bb &lt; a
  • 试试return std::tie(a-&gt;filenameSet_, a-&gt;filename_, a-&gt;path_) &lt; std::tie(b-&gt;filenameSet_, b-&gt;filename_, b-&gt;path_);

标签: c++ sorting c++11 vector segmentation-fault


【解决方案1】:
if(a->filename_ < b->filename_) {
    return true;
}
return a->path_ < b->path_;

这被称为“不是有效的严格弱排序”:

{ "a", "d" } < { "b", "c"}; because "a" < "b"
{ "b", "c" } < { "a", "d"}; because "c" < "d"

解决方法很简单:不要重新发明轮子:

return std::tie(a->filename_, a->path_) < std::tie(b->filename_, b->path_);

【讨论】:

  • filenameSet_ 的逻辑也可以绑定(假设它是 bool 而不是某种指针)。
  • @TravisGockel 是的。我有点不喜欢在bools 上使用&lt;,但是OP 中复杂的比较代码并没有那么好。
  • 绝对是双输。
  • @T.C.我同意代码有点复杂。这是一个非常古老的代码库的一部分,我想从使用与原始代码完全相同的行为开始。 std::tie 似乎完美地完成了这项工作。谢谢!
【解决方案2】:

@TC 的答案是一个很好的答案,因为该问题被标记为 C++11,但如果未来的读者追求 C++03 解决方案,编写这种比较运算符的“规范”方式(对于成员m1, m2, .... mn) 是

if (a->m1 != b-m1)
    return a->m1 < b->m1;
else if (a->m2 != b->m2)
    return a0>m2 < b->m2;
else ...
else if (a->mn != b->mn)
    return a->mn < b->mn;
else
    return false;

(请参阅here 进行讨论,问题具有上述风格但避免使用运算符!=)。

另一件事是您使用的是指针,而您的代码不是防弹的。如果 ab 为 NULL,您将遇到崩溃。如果您无法避免指针并且想要偏执,那么您需要添加检查,例如

if (!a && !b)
    return false;
else if (!a)
    return true;
else if (!b)
    return false;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 2019-11-17
    相关资源
    最近更新 更多