std::sort - 正在传递错误的比较器未定义的行为

std::sort - is passing a faulty comparator undefined behavior?

本文关键字:比较器 未定义 错误 sort std      更新时间:2023-10-16

请考虑以下代码:

std::sort(vec.begin(), vec.end(),
    [](const Foo& lhs, const Foo& rhs) { return !(lhs < rhs); }
);
如果 lhs == rhs,lambda(lhs, rhs

) 和 lambda(rhs, lhs) 都将返回 true,这违反了提供严格弱排序的要求。但是,该标准是否明确将通过这样的比较器标记为未定义的行为?

警告:极端的语言律师随之而来。

该标准的最新草案的措辞在[alg.sorting]p3中是这样说的:

对于所有需要Compare的算法,有一个版本使用operator<代替。也就是说,comp(*i, *j) != false默认为 *i < *j != false .对于 25.4.3 中描述的算法以外的算法,comp 应诱导对值进行严格的弱排序。

通过使用"应"一词,该标准含蓄地指出,违反它会导致未定义的行为。

是否要求给定函数对所有可能的值施加SWO,或者仅针对给定算法的值,这在标准中尚不清楚。但是,由于限制是在讨论这些特定算法的段落中陈述的,因此假设它指的是提供给算法的值范围并不是没有道理的。

否则,由于 NaN,默认operator<无法在 float 秒内强加 SWO。

这已经在 为什么 std::sort 崩溃如果比较函数不像运算符<?中得到了回答。

至少该线程中提出该问题的人声称收到了例外

为了从标准中总结一下,我强调了与问题相关的部分。现在,"正常工作"的反义词可以解释为"未定义的行为"。

25.3 排序及相关操作

  1. 25.3 中的所有操作都有两个版本:一个采用 Compare 类型的函数对象,另一个使用 操作员<。
  2. 比较用作函数对象,如果第一个参数小于第二个参数,则返回 true,如果返回 false 否则。比较 comp 始终用于假设排序关系的算法。假设 comp 不会通过取消引用的迭代器应用任何非常量函数。
  3. 对于所有采用比较的算法,有一个版本改用运算符<。也就是说,comp (*i, *j) != false 默认为 *i <*j != false。对于 25.3.3 中描述的算法以外的算法正常工作,comp 具有 以诱导对值进行严格的弱排序。
  4. 术语"严格"是指对非反身关系的要求(!comp (x, x) 表示所有 x),术语弱 要求不如总排序强,但比部分排序的要求强。如果我们 将 equiv(a, b) 定义为 !comp (a, b) && !comp (b, a),那么要求是 comp 和 equiv 都是 传递关系:
    • comp (a, b) && comp (b, c) 意味着 comp (a, c)
    • equiv(a, b) && equiv(b, c)
    • 表示 equiv(a, c) [ 注意:在这些条件下,可以证明
      1. 等价是等价关系
      2. comp 在等价类上诱导出由 equiv 确定的明确定义的关系
      3. 诱导关系是一个严格的总排序。 —尾注 ]

[alg.sorting]/3 用于 25.4.3 中描述的算法以外的算法工作 正确地,comp必须对值进行严格的弱排序。

[alg.sorting]/4 术语严格是指对所有x的不可反身关系(!comp(x, x))的要求......

您的比较谓词不是严格的弱排序。