In my talk “The Best Type Traits C++ Doesn’t Have”, I present is_trivially_equality_comparable (essentially “can I compare these objects at runtime with memcmp?”) and contrast it with P0732’s notion-now-perhaps-known-as has_strong_structural_equality (essentially “can I compare and/or stringify these objects at compile time by iterating over their bases and members?”).

In my Aspen presentation, I failed to talk about another motivation for memcmp-equality: std::atomic’s compare_and_exchange.

struct S {
    short a;
    short b;
    auto operator<=>(const S&) = default;

std::atomic<S> a{{1,2}};
S expected{1,2};
S desired{3,4};
a.compare_exchange_strong(expected, desired);

The atomic library code doesn’t actually use the type’s comparison operator to do the comparison — instead, it uses memcmp. This means that your atomic code is broken, unless your type happens to be trivially comparable.

Specifically, if your struct type has padding bits (like, if you change short a to char a in the code sample above), then the compare_exchange_strong will be doing a memcmp on the padding bits, which means it might actually fail when you expected it to succeed, semantically speaking.

(This bug is the subject of active proposal P0528, which proposes to solve it via built-in magic. I have not closely followed P0528’s progress, but it certainly sounds like I should be strongly opposed to its approach.)

I’d like to see the standard library gain a new type trait has_padding_bits<T>, which would immediately see very high adoption:

struct A {
    short a, b;
static_assert(!has_padding_bits_v<A>); // OK

struct B {
    char a;
    short b;
static_assert(!has_padding_bits_v<A>); // error

The next step would be to require !has_padding_bits_v<T> for any class type used as the template type parameter of std::atomic::compare_exchange_weak. And in fact I’d really like to require something along the lines of

 is_trivially_equality_comparable_v<T> ||
 (!is_equality_comparable_v<T> && !has_padding_bits_v<T>)

Sadly, I predict that if we required compare_exchange-able types to actually be comparable, it would break an intolerable amount of existing code.

Posted 2018-06-08