As long as your structure type doesn't have any padding, there's no explicit support for it in the standard, but support for something very close to it can be inferred.
Given a trivially copyable type T
, what's explicitly allowed is to copy its representation into an array of char
(or unsigned char
) and back.
There is no requirement that the contents of the array are preserved in the array itself. The contents could be stored in a file, and re-read on a subsequent execution of the program. Or stored in an object of a different type, so long as that type allows it. For this to work, implementations must allow memcpy
ing representations into objects when those representations did not originate from objects of type T
in that same run.
As a result, at the very least,
int main() {
vec v = {1.9f, 2.5f, 3.1f};
float a[3];
assert(sizeof v == sizeof a);
{ char tmp[3 * sizeof(float)];
std::memcpy(tmp, &v, 3 * sizeof(float));
std::memcpy(a, tmp, 3 * sizeof(float)); }
assert(a[0] == v.x);
assert(a[1] == v.y);
assert(a[2] == v.z);
vec u;
{ char tmp[3 * sizeof(float)];
std::memcpy(tmp, a, 3 * sizeof(float));
std::memcpy(&u, tmp, 3 * sizeof(float)); }
assert(u.x == a[0]);
assert(u.y == a[1]);
assert(u.z == a[2]);
}
should either fail on the first assert
, or pass. For any representation where it'd fail, it's trivial to create a function which happens to come up with that exact representation in unambiguously valid ways, therefore it mustn't fail.
Now, omitting tmp
here is a bit iffy.
std::memcpy
is just repeated assignments of the individual bytes and could have been spelled out explicitly. The semantics of the =
operator imply that for trivially copyable types, a = b;
and { auto tmp = b; a = tmp; }
are equivalent. Same with a = b; c = d;
and { auto tmp1 = b; auto tmp2 = d; a = tmp1; c = tmp2; }
, and so on. The former is what a direct memcpy
does, the latter is what two memcpy
s through tmp
do.
On the other hand, the permission to copy in and out of an array of char
could be read as requiring an actual array of char
, not merely the functional equivalent of it.
Personally, I probably would not worry about that unless I actually came across an implementation which uses that interpretation, but if you want to play it safe, you can introduce such a temporary array, and verify that your compiler manages to optimise it away.