# Types that falsely advertise trivial copyability

I’ve drafted a paper for the latest WG21 mailing containing the following interesting examples, and proposing (as yet, vaguely) that we should do something about it.

## A type that falsely advertises trivial copyability

struct Plum {
int *ptr_;
explicit Plum(int& i) : ptr_(&i) {}
Plum(const Plum&) = default;
void operator=(const volatile Plum&) = delete;
template<class=void> void operator=(const Plum& rhs) {
*ptr_ = *rhs.ptr_;
}
};
static_assert(std::is_trivially_copyable_v<Plum>);
static_assert(std::is_assignable_v<Plum&, const Plum&>);
static_assert(!std::is_trivially_assignable_v<Plum&, const Plum&>);

int main() {
int a[3] = {1,2,3};
int b[3] = {4,5,6};
Plum pa[3] = {Plum(a[0]), Plum(a[1]), Plum(a[2])};
Plum pb[3] = {Plum(b[0]), Plum(b[1]), Plum(b[2])};
std::reverse_copy(pa, pa+3, pb);
assert(b[0] == 3 && b[1] == 2 && b[2] == 1);
std::copy(pa, pa+3, pb);
assert(b[0] == 1 && b[1] == 2 && b[2] == 3);
}


Microsoft STL’s std::pair<int&, int&> uses this trick, which I’ve heard credited to Peter Dimov and/or Eric Fiselier. We advertise Plum as a trivially copyable type, but in fact we give it semantics that are not value-semantic. This breaks libstdc++’s copy and Microsoft STL’s reverse_copy.

## A trivially copy-assignable type that breaks the world

struct Cat {};
struct Leopard : Cat {
int spots_;
Leopard& operator=(Leopard&) = delete;
using Cat::operator=;
};
static_assert(std::is_trivially_copyable_v<Leopard>);
static_assert(std::is_trivially_copy_assignable_v<Leopard>);

void test(Leopard& a, const Leopard& b) { a = b; }


This breaks every STL vendor’s copy — even libc++, which goes out of its way to double-check that the type it’s copying is not merely trivially_copyable but also trivially_copy_assignable. This type does in fact have a trivial copy-assignment operator, which is found by overload resolution. The trick is, it has the wrong type’s trivial copy-assignment operator!

## A non-TC aggregate containing only TC types

struct Sunglasses {
Plum plum_;
};
static_assert(!std::is_trivially_copyable_v<Sunglasses>);


You might be surprised that you can detect such “false advertising” simply by wrapping the type in an aggregate. GCC gets this wrong, but Clang+MSVC+EDG get it right.

This simple trick doesn’t quite work on Leopard:

struct PerilSensitiveSunglasses {
Leopard leopard_;
};
static_assert(std::is_trivially_copyable_v<PerilSensitiveSunglasses>);
static_assert(!std::is_copy_assignable_v<PerilSensitiveSunglasses>);
static_assert(!std::is_assignable_v<PerilSensitiveSunglasses&, PerilSensitiveSunglasses&>);


MSVC gets this wrong, but Clang+GCC+EDG get it right.

## A TC aggregate containing only non-TC types

Sometimes two wrongs make a right (Godbolt):

struct Fenris {
Fenris(Fenris&&) = default;
Fenris(const Fenris&);
Fenris& operator=(Fenris&&) = default;
Fenris& operator=(const Fenris&) = delete;
};
struct MoonMoon {
MoonMoon(MoonMoon&&) = default;
MoonMoon(const MoonMoon&) = delete;
MoonMoon& operator=(MoonMoon&&) = default;
MoonMoon& operator=(const MoonMoon&);
};

static_assert(!std::is_trivially_copyable_v<Fenris>);
static_assert(!std::is_trivially_copyable_v<MoonMoon>);

struct You {
Fenris f_;
MoonMoon m_;
};
static_assert(std::is_trivially_copyable_v<You>);


Clang+GCC get this wrong, but MSVC+EDG get it right.