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.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • 1
    This is a followup to [discussion in comments](https://stackoverflow.com/questions/56463341/how-to-use-memset-in-c/56464048?noredirect=1#comment105828377_56464048). https://godbolt.org/z/jBeRwD example showing that being an Aggregate seems to matter (public vs. private members). Other clues include https://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. – Peter Cordes Jan 22 '20 at 03:09
  • 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. – Henri Menke Jan 22 '20 at 03:10
  • 1
    @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. – BeeOnRope Jan 22 '20 at 03:11
  • @BeeOnRope Well, of course, but that is also not what `memcpy` is supposed to do. It literally copies memory. – Henri Menke Jan 22 '20 at 03:13
  • 2
    @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. – BeeOnRope Jan 22 '20 at 03:16
  • @BeeOnRope: How would you get two pointers/references to two objects of the same type to overlap? – Nicol Bolas Jan 22 '20 at 03:54
  • 1
    @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. – BeeOnRope Jan 22 '20 at 04:04

1 Answers1

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 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. – BeeOnRope Jan 22 '20 at 03:59
  • 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. – BeeOnRope Jan 22 '20 at 04:03
  • 1
    @BeeOnRope: Don't forget: "point to ***distinct*** `T` objects" – Nicol Bolas Jan 22 '20 at 04:05
  • 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. – BeeOnRope Jan 22 '20 at 04:24
  • 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. – BeeOnRope Jan 22 '20 at 04:27
  • 1
    @BeeOnRope: Well, you could require that `T` is declared `final` and thus cannot be a base class subobject. But that's about it. – Nicol Bolas Jan 22 '20 at 04:28
  • Thanks. Makes [this trait](https://en.cppreference.com/w/cpp/types/is_final) unexpectedly useful. – BeeOnRope Jan 22 '20 at 04:29