Under what conditions is it safe to use std::memcpy to copy between objects?
Asked Answered
C

1

4

Under what set of conditions is it safe to use std::memcpy to copy from one object to another?

For example, what conditions must T, src and dest satisfy for the following to be safe:

template <typename T>
void copy_bytewise(T& dest, const T& src) {
  std::memcpy(&dest, &src, sizeof(T));
}

The only thing we can assume about src and dest is that they don't overlap1. In particular either of src or dest may be a reference to a member or base class.

I am interested in answers which refer to the standard, but if this diverges from common practice (e.g., the de-facto C++ ABI from Itanium) I'd also like to know.

Note that T satisfying the TriviallyCopyable (TC) concept is not sufficient, as this example shows. base is TC yet not memcpy-safe (due to re-use of padding for members of a derived class).

I am especially interested if there is any condition on T alone that is sufficient (and not necessarily necessary), without requiring conditions on src and dest (that cannot, in general, be statically determined).


1 Specifically, my assumption is that if they do overlap, they are still safe to copy under the same conditions on T as for std::memcpy, but using std::memmove instead. If assumption is incorrect, it could be part of an answer.

Crary answered 22/1, 2020 at 3:6 Comment(7)
This is a followup to discussion in comments. godbolt.org/z/jBeRwD example showing that being an Aggregate seems to matter (public vs. private members). Other clues include itanium-cxx-abi.github.io/cxx-abi/abi.html#POD mention of when that ABI decides to put derived objects in the padding of the base class or not based on when anything that could break that being UB in all versions of the ISO C++ standard.Caril
Isn't the point of memcpy that it doesn't have requirements on the types? It is the only way in C++ to safely perform type-punning.Prot
@HenriMenke, eh no. Using memcpy has fairly strict requirements on the types involved, or else it will do damage. Consider any type a user-defined copy constructor: obviously just copying the bytes over is unlikely to do what the copy constructor would have done.Crary
@Crary Well, of course, but that is also not what memcpy is supposed to do. It literally copies memory.Prot
@HenriMenke - I think this "language lawyer" question is not for you, it requires a lot of background knowledge on UB, memcpy, the various type concepts in C++, etc. It's hard to even answer your questions/comments without assuming a lot of existing background. I recommend background reading of the linked and related questions.Crary
@BeeOnRope: How would you get two pointers/references to two objects of the same type to overlap?Neveda
@NicolBolas - I guess the most obvious, and possibly only, example would be if they are the same object (i.e., "overlap" but not "proper overlap"). In that case, it is my understanding that std::memcpy is not allowed regardless of anything else.Crary
N
5

From [basic.types]/3:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes ([intro.memory]) making up obj1 are copied into obj2, obj2 shall subsequently hold the same value as obj1.

In short:

what conditions must T satisfy for the following to be safe

T must be trivially copyable; that's the only condition that T must satisfy. The other requirement is not a restriction on T, but a restriction on the nature of the objects potentially being copied. Which means it's not something you can statically determine.

Neveda answered 22/1, 2020 at 3:50 Comment(7)
Thanks. So, in practice, for the copy_bytewise function I wrote, there is no trait, existing or contemplated, that would make that safe for any T, because of the base class issue? I should be clear that I was asking about restrictions of on instances of (sub)objects of type T, although I didn't do a good job of writing it, and it affected a bit the language you used in your answer. I am making a small (in characters) edit to clarify and you may want to update your answer.Crary
I have changed the language "conditions on T" to "conditions on T, src and dest" which I think is clearer. LMK if you think the change is too big. I think your answer still completely applies, but just some of the language could be updated.Crary
@BeeOnRope: Don't forget: "point to distinct T objects"Neveda
Is there a stronger condition on T that removes the need for additional (dynamic) conditions on src and dest objects? In practice, there seems to be (e.g., gcc overwrites padding sometimes even when the dest object isn't known), but I don't know if this is covered by the standard or if there is a trait for it.Crary
If you think that deserves a separate question, LMK - I don't want to diverge too far from the original text since that would be unfair to your existing answer.Crary
@BeeOnRope: Well, you could require that T is declared final and thus cannot be a base class subobject. But that's about it.Neveda
Thanks. Makes this trait unexpectedly useful.Crary

© 2022 - 2024 — McMap. All rights reserved.