如果比较函数不是运算符 <,为啥 std::sort 会崩溃?
Posted
技术标签:
【中文标题】如果比较函数不是运算符 <,为啥 std::sort 会崩溃?【英文标题】:Why will std::sort crash if the comparison function is not as operator <?如果比较函数不是运算符 <,为什么 std::sort 会崩溃? 【发布时间】:2021-11-19 13:42:45 【问题描述】:以下程序是用VC++ 2012编译的。
#include <algorithm>
struct A
A()
: a()
bool operator <(const A& other) const
return a <= other.a;
int a;
;
int main()
A coll[8];
std::sort(&coll[0], &coll[8]); // Crash!!!
如果我将return a <= other.a;
更改为return a < other.a;
,那么程序将按预期运行,没有任何异常。
为什么?
【问题讨论】:
std::sort
的比较器需要严格的弱排序,<=
不提供。
你应该为 A ctor 写 a(0)...但它不会在这里崩溃!
@LaszloPapp:是的。它对a()
进行值初始化(这就是a()
的含义),对于int
而言,这意味着0。
@LaszloPapp: ***.com/questions/14259602/…
请将未定义行为 &coll[8] 替换为 coll + 8
【参考方案1】:
std::sort
需要一个满足 严格弱排序 规则的排序器,对此进行了解释
here
所以,你的比较器说 a < b
a == b
不遵循 严格弱排序 规则,算法可能会崩溃,因为它会进入无限循环。
【讨论】:
+1 这很好地说明了std::sort
的比较器的要求。如果您的编译器与 C++11 兼容(并且 OP is 兼容;VS2012),我将使用 std::sort(std::begin(coll), std::end(coll));
来遵循这一点。如果您曾经更改数组的尺寸,或者改用任何标准容器,您会很高兴的。
进入无限循环只会导致它卡住。相反,它转储核心。为什么?
@btilly 我认为是因为std::sort
使用递归算法,在无限循环的情况下会导致堆栈溢出。
您必须详细了解它检查的内容,但标准的排序例程旨在运行得非常非常快,因此它们不会检查您所做的一切以查看是否正常,他们只是依靠它。如果你的比较返回了不可能的结果,那么不可能的事情就会发生——比如说它得到了一些比较的结果,并且它使用它作为查找位置的索引,只有它“知道”哪些值是可能的并且“知道”结果引用将在有效存储中,因此它只是获取它。 Kaboom:SIGSEGV 运气好。如果运气不好,它会默默地处理您的数据。【参考方案2】:
xorguy 的答案还不错。
我只想从标准中添加一些引用:
25.4 排序及相关操作[alg.sorting]
为了使 25.4.3 中描述的算法以外的算法正常工作,comp 必须对值引入 严格的弱排序。
术语 strict 指的是非自反关系的要求(!comp(x, x) 对于所有 x),术语 weak 指的是非自反关系的要求与全排序一样强,但比部分排序强。
所以 xorguy 解释得很好:你 comp
函数说 a < b
当 a == b
不遵循 严格弱排序 规则...
【讨论】:
【参考方案3】:您的代码的问题是您正在访问无效的内存。代码
coll[8]
试图访问最后一个数组元素之后的元素(最后一个元素索引为 7)。 我建议使用 std::array 代替纯 C 数组。
std::array<A, 8> a;
// fill it somehow
std::sort(a.begin(), a.end());
【讨论】:
以上是关于如果比较函数不是运算符 <,为啥 std::sort 会崩溃?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 `std::initializer_list` 不提供下标运算符?
为啥 std::apply 可以调用 lambda 而不是等效的模板函数?
为啥 std::fstream 返回 void 而不是 bool
为啥 std::compare_three_way 不是模板结构/函子