C++20中std::strong_ordering是否要求全序?我的RPS枚举实现合规吗?
问题
我为代表剪刀石头布的枚举类Hand实现了三路比较运算符operator<=>,返回std::strong_ordering。该实现可编译且能正确判断游戏胜负,但存在循环比较(Paper<Scissors、Scissors<Rock、Paper>Rock),破坏了传递性,无法用于排序或std::set等场景,会导致未定义行为。我注意到C++标准草案N4950的注释指出std::strong_ordering对应数学中的全序,但未找到主文中明确的规范性要求。请问std::strong_ordering是否被正式要求必须表示全序?若要求全序,我的实现是否因破坏传递性而无效?
回答
一、std::strong_ordering的规范性要求
C++标准(包括N4950草案)对std::strong_ordering的约束是明确的:它必须对应全序关系,这不是注释的指导性内容,而是标准正文的隐含强制要求:
- 标准定义
std::strong_ordering属于全序比较范畴,要求其对应的比较操作满足全序的三大公理:- 自反性:
a <= a始终成立 - 反对称性:若
a <= b且b <= a,则a == b - 传递性:若
a <= b且b <= c,则a <= c
- 自反性:
- 所有依赖比较结果的标准库组件(如
std::sort、std::set、std::map)都要求比较操作满足全序,而std::strong_ordering正是用来标识这种符合全序要求的比较结果,这是其设计的核心目的。
二、你的实现是否无效?
是的,你的实现完全不符合std::strong_ordering的要求:
- 剪刀石头布的胜负逻辑是循环偏序,不满足传递性,本质上不属于全序关系,因此不能用
std::strong_ordering作为operator<=>的返回类型。 - 虽然代码能编译通过,但一旦将其用于依赖全序的标准库组件(比如向
std::set<Hand>插入元素),会直接触发未定义行为——标准库不会处理这种违反全序的情况,程序可能出现崩溃、错误输出或其他不可预测的异常。
三、正确的处理方式
应该将胜负判断和排序逻辑拆分实现:
- 单独编写胜负判断函数,比如
bool beats(const Hand& other) const,专门处理剪刀石头布的循环胜负逻辑。 - 若需要对
Hand进行排序或存入有序容器,单独实现满足全序的三路比较(比如按枚举值的声明顺序定义:Rock < Paper < Scissors),此时返回std::strong_ordering才是合法有效的。
内容的提问来源于stack exchange,提问作者yqZhang4480
相关产品推荐
相关产品推荐

