关于std::ranges::sort对仅支持partial_ordering且含运行时无序元素的范围排序结果是否明确的问询
问询:std::ranges::sort对仅支持partial_ordering且含运行时无序元素的范围排序结果是否明确?
嘿,最近在捣鼓C++20的范围排序时碰到个挠头的问题,想跟大家确认下:当我用std::ranges::sort去排序一个元素类型仅支持std::partial_ordering,而且范围里还存在运行时彼此无序的元素时,最终的排序结果是明确可预测的吗?
先给大家看个我写的测试用的结构体例子:
#include <cassert> #include <algorithm> #include <compare> #include <iostream> #include <optional> #include <vector> struct Foo { int val; bool is_unordered; Foo(int v, bool unord = false) : val(v), is_unordered(unord) {} friend auto operator<=>(Foo const& lhs, Foo const& rhs) { // 两个标记为无序的元素之间返回unordered if (lhs.is_unordered && rhs.is_unordered) { return std::partial_ordering::unordered; } // 其他情况按int的顺序比较 return lhs.val <=> rhs.val; } friend bool operator==(Foo const& lhs, Foo const& rhs) { auto cmp = lhs <=> rhs; return cmp == std::partial_ordering::equal || cmp == std::partial_ordering::unordered; } };
比如我创建这样一个容器:
std::vector<Foo> vec = {Foo(3), Foo(1, true), Foo(2), Foo(1, true)}; std::ranges::sort(vec);
这时候排序后的结果到底是确定的吗?我查了下相关规则,给大家捋一捋:
- 首先,
std::ranges::sort的核心要求是:比较器必须在元素上构成严格弱序(strict weak ordering)。这个要求是硬约束,标准里明确规定了,如果违反的话,程序行为就是未定义的。 - 当你的元素比较返回
std::partial_ordering::unordered时,这就打破了严格弱序的要求——严格弱序要求任意两个元素之间,要么a小于b,要么b小于a,要么两者等价,不存在“无序”这种第三种情况。 - 所以如果直接用这种返回partial_ordering且存在运行时无序对的比较关系去调用
std::ranges::sort,排序结果完全是不可预测的,可能每次运行都不一样,甚至可能出现奇怪的行为,标准不会给你任何保证。
那如果我就是要排序这类元素怎么办?其实很简单,自己写个比较器,把“无序”的情况转化为等价关系或者某种固定顺序,让比较器满足严格弱序就行。比如:
std::ranges::sort(vec, [](const Foo& a, const Foo& b) { auto cmp = a <=> b; // 把无序的元素视为等价,不进行交换 if (cmp == std::partial_ordering::unordered) { return false; } return cmp == std::partial_ordering::less; });
这时候比较器满足严格弱序了,排序后的结果就是明确可预测的了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

