admin管理员组文章数量:1310455
I am implementing a three-way comparison (operator<=>) for an enum class representing Rock-Paper-Scissors. My implementation compiles and returns std::strong_ordering
, but I am unsure whether it meets the formal requirements for std::strong_ordering
, particularly regarding transitivity.
Here is my code:
#include <compare>
enum class Hand { Rock, Paper, Scissors };
std::strong_ordering operator<=>(Hand a, Hand b) {
if (a == Hand::Paper && b == Hand::Scissors) return std::strong_ordering::less;
if (a == Hand::Scissors && b == Hand::Rock) return std::strong_ordering::less;
if (a == Hand::Rock && b == Hand::Paper) return std::strong_ordering::less;
if (a == b) return std::strong_ordering::equal;
return std::strong_ordering::greater;
}
This implementation introduces a cyclic comparison:
Paper < Scissors
Scissors < Rock
Paper > Rock
, breaking transitivity.
I used std::strong_ordering
as the return type, since any two values of type enum class Hand
are compariable, and all equal values of this type are same.You can see that this code can be compile, and works well on judging the winner of the game. Of course, I can't put a series of values of this enum class into a sort function or
std::set
, etc., which introduces an undefined behavior.
I noticed that the C++ draft standard (N4950, §17.11.2[cmp.categories.pre]/2) contains a note stating that "The type strong_ordering
corresponds to the term total ordering in mathematics." However, since this is only a note, I could not find an explicit normative requirement in the main text that std::strong_ordering
must always represent a total order, particularly the transitivity requirement.
Is std::strong_ordering
formally required to enforce a total order, or is the note just an informal explanation?
If a total order is required, is my implementation invalid because it breaks transitivity?
I am implementing a three-way comparison (operator<=>) for an enum class representing Rock-Paper-Scissors. My implementation compiles and returns std::strong_ordering
, but I am unsure whether it meets the formal requirements for std::strong_ordering
, particularly regarding transitivity.
Here is my code:
#include <compare>
enum class Hand { Rock, Paper, Scissors };
std::strong_ordering operator<=>(Hand a, Hand b) {
if (a == Hand::Paper && b == Hand::Scissors) return std::strong_ordering::less;
if (a == Hand::Scissors && b == Hand::Rock) return std::strong_ordering::less;
if (a == Hand::Rock && b == Hand::Paper) return std::strong_ordering::less;
if (a == b) return std::strong_ordering::equal;
return std::strong_ordering::greater;
}
This implementation introduces a cyclic comparison:
Paper < Scissors
Scissors < Rock
Paper > Rock
, breaking transitivity.
I used std::strong_ordering
as the return type, since any two values of type enum class Hand
are compariable, and all equal values of this type are same.You can see that this code can be compile, and works well on judging the winner of the game. Of course, I can't put a series of values of this enum class into a sort function or
std::set
, etc., which introduces an undefined behavior.
I noticed that the C++ draft standard (N4950, §17.11.2[cmp.categories.pre]/2) contains a note stating that "The type strong_ordering
corresponds to the term total ordering in mathematics." However, since this is only a note, I could not find an explicit normative requirement in the main text that std::strong_ordering
must always represent a total order, particularly the transitivity requirement.
Is std::strong_ordering
formally required to enforce a total order, or is the note just an informal explanation?
If a total order is required, is my implementation invalid because it breaks transitivity?
Share Improve this question edited Feb 11 at 15:30 TylerH 21.1k77 gold badges79 silver badges112 bronze badges asked Feb 3 at 11:18 yqZhang4480yqZhang4480 5422 silver badges8 bronze badges 12 | Show 7 more comments1 Answer
Reset to default 11Your <=>
that returns the type std::strong_order
does not have to model the concept totally-ordered, but it is a good idea for it to do so.
Ie, an empty program with such a <=>
and no use of it is well-formed. Once you do almost anything with it, your program is no longer valid.
As an example, almost any use of a strong order in any algorithm written by someone else is going to require it modelling three-way-comparable, which in turn requires you to model totally_order
, which in turn requires transitive comparisons.
[cmp.concept]/2:
Let a and b be lvalues of type const remove_reference_t<T>. T and Cat model three_way_comparable<T, Cat> only if
...
(2.8) if Cat is convertible to strong_ordering, T models totally_ordered ([concept.totallyordered]).
[concept.totallyordered]/1:
Given a type T, let a, b, and c be lvalues of type const remove_reference_t<T>. T models totally_ordered only if
...
(1.2) If bool(a < b) and bool(b < c), then bool(a < c).
Your <=>
fails [concept.totallyordered]/1.2 as follows:
Let a be Rock, b be Paper, and c be Scissors. Rock < Paper, Paper < Scissors, thus to model totally_ordered Rock must be less than Scissors, which is false; so your type does not model totally_ordered.
As three-way-comparable when Cat is strong_ordering
requires it to model totally_ordered
, your <=>
does not produce a valid three-way-comparable object.
To be more concrete, if you stick your class into a variant, <=>
is only valid on that variant if your strong order models three_way_comparable which in turn requires a total order, which yours is not.
You are not required by the standard that your std::strong_order
<=>
be non-garbage; simply creating such a <=>
does not result in undefined behavior. But every bit of code generation from std
, be it a sorting algorithm or putting your type into a container, will presume that your <=>
models three-way-comparable, which yours does not, which in turn will lead very rapidly to errors at best and UB at worst.
Making a <=>
not model three-way-comparable is a bit like making operator->
do something unrelated to dereferencing a pointer. Legal, but almost always a bad idea.
本文标签: cDoes stdstrongordering in C20 require a total orderStack Overflow
版权声明:本文标题:c++ - Does std::strong_ordering in C++20 require a total order? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741825849a2399635.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
strong_ordering
are the same as that for a total order in the mathematical sense. There does not have to be more normative text that makes them the same. – j6t Commented Feb 3 at 12:00strong_ordering
as the same as total ordering was exaggerated. – Oersted Commented Feb 3 at 12:21strict_weak_order
!=weak_ordering
though... – Jarod42 Commented Feb 3 at 13:36