https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78420
--- Comment #28 from Jonathan Wakely <redi at gcc dot gnu.org> --- I can commit a patch that fixes the original testcase, but I now think that the resolution of https://wg21.link/lwg2450 requires compiler help of some kind, at least for cases like comment 27. It effectively says "if x < y uses the built-in operator< for pointers, that comparison yields a total order". That's very different to "if x and y are pointers, comparing them yields a total order". The library can't tell whether x < y uses a built-in operator for pointers, because there could be conversions involved, so the library can't decide whether the cast to uintptr_t is needed. (In reply to Jakub Jelinek from comment #21) > So, any progress here on either the standard side or GCC side? Do you still > need a builtin for constexpr compatible reinterpret cast from the FE, or can > it be solved solely in the library? Even such a magic cast doesn't help, because the library can't tell when that cast should be used. We can't unconditionally cast everything to uintptr_t, only pointers, but we can't tell which operands convert to pointers. One solution would be a new __builtin_less(op1, op2) builtin that does all the required work: - if overload resolution for op1 < op2 results in comparing two pointers, then return the result of (uintptr_t)op1 < (uintptr_t)op2 instead, in a constexpr-compatible way (including rejecting comparisons that aren't allowed in constant expressions); - otherwise, just evaluate op1 < op2 as normal, which may or may not be constexpr-compatible, depending on decltype(op1) and decltype(op2). Then std::less would just use __builtin_less(x, y) and everything is done by the compiler. And similarly for __builtin_greater/greater_equal/less_equal. Another solution might be __builtin_totally_ordered(op) which results in some tree type that wraps op, and when used as the operand of a relational operator it performs overload resolution as would happen for op, and if that would compare pointers then op is converted to (uintptr_t)op and that is compared instead. Then std::less would use: return __builtin_totally_ordered(x) < __builtin_totally_ordered(y); But this seems a lot more work to implement, with no advantages. Another solution would be to change the standard, so that std::less<void> only requires a total order when the arguments are pointers, and not for arbitrary types that might convert to pointers.