When is a trivially copyable object not trivially copyable?
Subtitled, “The Itanium ABI ruins everything, again.”
Answer number one: A trivially copyable object is not trivially copyable
when it is volatile
. Godbolt:
struct S {
volatile int i;
};
static_assert(std::is_trivially_copyable_v<S>);
void foo(S *dst, S *src, int n) {
std::copy_n(src, n, dst);
}
Both libc++ and libstdc++ have implementations of std::copy_n
that
optimize volatile loads and stores into memcpy
, which causes tearing
of reads and writes.
I have written a draft proposal [EDIT 2019-04-13: published as P1153R0 “Copying volatile subobjects is not trivial”] — coauthored with JF Bastien, and seeking as many coauthors as possible! — to solve this problem once and for all.
Answer number two: A trivially copyable object is not trivially copyable when it is a potentially overlapping subobject. Wandbox:
struct A { int a; };
struct B : A { char b; };
struct C : B { short c; };
static_assert(!std::is_standard_layout<B>::value, "");
static_assert(std::is_trivially_copyable<B>::value, "");
int main()
{
C c1 { 1, 2, 3 };
B& dst = c1;
const B& src = C{ 5, 6, 7 };
printf("before operator=: %d\n", int(c1.c)); // 3
dst = src;
printf("after operator=: %d\n", int(c1.c)); // 3
printf("before std::copy: %d\n", int(c1.c)); // 3
std::copy_n(&src, 1, &dst);
printf("after std::copy: %d\n", int(c1.c)); // 7
}
I’m not yet sure what is the right fix for this issue at the WG21 level, but I believe it deserves to be fixed somehow.