import std;
class obj_t {
private:
int8_t val;
// An object that stores the sign. BUT I define the inequality as
// -ve sign < 0 < +ve sign < -ve sign.
// My < and > operators here are symmetric and reflexive NOT transitive.
// It is somewhat like rock paper scissor with equality defined.
public:
bool operator == (const obj_t& B) const {
return ((val > 0 && B.val > 0) || (val < 0 && B.val < 0) || (val == B.val && val == 0));
}
auto operator <=> (const obj_t& B) const {
if (val > 0) {
if (B.val > 0) return (0 <=> 0);
else if (B.val < 0) return (0 <=> 1);
else return (1 <=> 0);
}
else if (val < 0) {
if (B.val > 0) return (1 <=> 0);
else if (B.val < 0) return (0 <=> 0);
else return (0 <=> 1);
}
else return (val <=> B.val);
}
void fill(double n) {
// Stores the sign. double -- to accept a large range of any type of number.
if (n > 1.0e-10) val = 1;
else if (n < -1.0e-10) val = -1;
else val = 0;
return;
}
};
int main(void) {
std::println("{}", std::totally_ordered<obj_t>);
// Prints 'true' -- Meaning the concept fails to catch the
// unmet "transitivity" condition for total ordering.
return 0;
}
C++23: I just created, as the comments in the above code suggest, a class which stores only three values (-1, 0, 1). This is a "rock paper scissors" kind of class, for simplicity.
The ordering is individually defined as -1 < 0 and 0 < 1 but 1 < -1. (I compared based on sign but really this object is just three values) This < is symmetric too. Equality is reflexive, and that works. However, this is NOT transitive.
Meaning: this is not a well-ordered class (objects here are not well ordered) let alone total or partial ordering.
But why does the program after being executed print "true" after this? Does this mean that C++20 concepts library does not really check for order but just checks if the spaceship operator is overloaded (or rather all of < <= > >=) ? Does the compiler even check for symmetry? Or reflexivity?
Moreover does this not mean that even after concepts it is unreliable to use custom-defined structs with custom comparators in a sorted std::vector or std::map? Like we are supposed to think of all cases ourselves rather than compilers doing the job? (Then what is the purpose of concepts?)
std::totally_ordered<obj_t>only tells you whether the concepttotally_orderedis satisfied, meaning whether or not the type syntactically satisfies the requirements of the type. That's only testing whether expressions that a totally ordered type should be usable with are well-formed for that type.Whether the type also models the concept, i.e. whether these expressions satisfy the semantic requirements of the concept, is not checked by an expression of the form
std::totally_ordered<obj_t>and is also generally not checked by library function. If you pass a type to a function that requires the concept to be modelled, then it is your responsibility to assure that it does so.It is fundamentally impossible to verify that the semantic requirements are satisfied. That would in general be an undecidable problem and so we can't possibly ask the compiler to verify it. It is also not the intention of concepts to do any analysis of definitions at all. It acts purely on the basis of declarations. (C++ is designed so that there is generally no guarantee that a compiler must see definitions since they can be in other translation units.)