17

In another question I incorrectly used the term POD to refer to data types that aren't actually POD types (on account of having a constructor). Now, I've looked through the standard and couldn't find a proper name for what I want. Nor can I actually find a guarantee that copying is actually allowed.

The data type I mean is a POD, but may contain functions, including constructors, but nothing that should alter its alignment or size characteristics when compared to an equivalent POD type.

In section 3.9 of the standard it states that POD data can be copied with memcpy, either to another object, or to character data and back. No such guarantee is ever made of non-POD data.

However, the object representation of an object is defined in the same section. It is defined such that one would believe any two objects of the same type could be safely copied via memcpy.

So my questions are:

  1. Is the copy with memcpy actually guaranteed to be safe for such objects?
  2. If yes, then why is there a special note about memcpy and POD?
  3. Is there a name for this type of data which is memcpy safe?

A simple example of the type of object I mean:

struct ex_struct
{
  int a,b,c,d;
  ex_struct() : a(123) { }
}

Reading the C++0x draft, my struct would appear to be a trivially copyable class (9.1). I believe that implies memcpy would be safe.

Community
  • 1
  • 1
edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
  • If the type in question is guaranteed to behave like a POD (in your example it is), then although it is not a POD, you should be fine. But let's wait for the language lawyers to tell us. – Alexandre C. Mar 25 '11 at 09:52
  • 1
    The other question is - Why do you think there is an advantage in using memcpy, and why do you think the compiler will not see that? – Bo Persson Mar 25 '11 at 10:08
  • 1
    remove the constructor, and move the member initialization into a `init()` member function, and your type will be a true POD – smerlin Mar 25 '11 at 10:14
  • We use the term *fully stack based* for the lack of a better expression to indicate that none of the internal storage is dynamic. – Nim Mar 25 '11 at 10:19
  • 6
    @smerlin: Except that that is a _horrid_ idea. – Lightness Races in Orbit Mar 25 '11 at 10:26
  • 2
    @Nim: _fully statically-allocated_ would be better. C++ doesn't care where such objects physically go. – Lightness Races in Orbit Mar 25 '11 at 10:26
  • Why not use `std::copy`? – Lightness Races in Orbit Mar 25 '11 at 10:29
  • @Tomalak, I did say, for the lack of a better expression, I wonder if *statically-allocated* would conjure up images of *static* members... – Nim Mar 25 '11 at 10:33
  • @Tomalak, the reason to use such objects and memcpy is that you can trivially serialize them, copy them in/out from any type of memory, and handle them without knowledge of the contained data. This are great features for sharing data over the network, in shared memory, or even between threads. – edA-qa mort-ora-y Mar 25 '11 at 10:34
  • 2
    @edA-qa mort-ora-y: Not really. You're flagrantly ignoring padding, endianness and type width across platforms. You also didn't address my question: why not use `std::copy`? – Lightness Races in Orbit Mar 25 '11 at 10:35
  • @Tomalak, if Platform A is binary identical to Platform B this is totally safe. In particular, processes/threads on the same machine, and machine identical nodes in a cluster. I would never suggest this is a good way to do an open API or anything. Can't use `std::copy` since one of the things doing the copying has a rather opaque view of the data (just a pointer and length). – edA-qa mort-ora-y Mar 25 '11 at 10:40
  • @edA-qa mort-ora-y: Yea, "if". I still don't understand why you can't use `std::copy`. Never heard of a copy constructor? You can control what gets copied and how. This is the canonical way to copy objects. – Lightness Races in Orbit Mar 25 '11 at 10:41
  • @Tomalak, to *serialize* to a buffer (for transmission for example) you can use `std::copy` (akin to `memcpy`), however I don't see what a copy constructor has to do with *serialization*... – Nim Mar 25 '11 at 10:48
  • 1
    @Nim: No, nor do I. Then again, neither the question title nor the question text state that the OP is intending serialization.. even once. – Lightness Races in Orbit Mar 25 '11 at 10:50
  • @Tomalak, it's definitely missing in the question (and his previous question), but from what I gather, this is what the OP is after... – Nim Mar 25 '11 at 10:56
  • If you are on the same machine, other thread or shared memory, a copy constructor or assignment will work fine. Using std.:copy, for example. If you are going to send data over the network, what do you gain from memcpy? Speed?! – Bo Persson Mar 25 '11 at 13:43
  • @Bo, Consider that all the structures are actually different but with a common header component that includes their size. Thread/share memory logic works basically like a network connection and has a generic message handler to cope with all types. So you can't use std::copy, or copy constructor since you don't know what the type actually is. – edA-qa mort-ora-y Mar 25 '11 at 16:52
  • @edA-qa: Thread/share memory logic doesn't _have_ to work like that. It sounds like you're writing "C with classes", not C++. – Lightness Races in Orbit Mar 25 '11 at 22:30

4 Answers4

7

In C++0x, the concept of PODness is broken out into several individually useful categories:

A trivially copyable class is a class that (draft 3242, section [class]):

  • has no non-trivial copy constructors (12.8),
  • has no non-trivial move constructors (12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and
  • has a trivial destructor (12.4).

A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.

[ Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. — end note ]

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

The requirements for trivial constructors, assignment operators, and destructor are scattered throughout section 12 "Special Member Functions" [special].

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 2
    So my struct is *trivially copyable*, meaning I may use *memcpy* on it. This also means that if I derive from such a class I can still remain *trivially copyable* so long as my derived class would also be. – edA-qa mort-ora-y Mar 28 '11 at 04:09
  • 1
    Yes, C++0x guarantees that it is safe to use `memcpy` on instances of such a class. As long as all your subobjects (including base classes) are trivially-copyable, your composite also will be by default (until you start adding copy-constructors, etc). – Ben Voigt Mar 28 '11 at 04:12
  • If my class is Standard Layout then can I use `memcpy` for manipulate with objects of such class? – Konstantin Burlachenko Oct 18 '18 at 09:19
4

The notion of POD in C++03 is too strict indeed. In C++0x POD is generalized to include the objects you described too. So don't worry, you can name it POD. See a nice summery on Wikipedia.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 2
    The notion of POD isn't significantly changed, but a new notion, layout compatible, was introduced which covers his case. – James Kanze Mar 25 '11 at 10:09
  • according to that wikipedia article, the `ex_struct` type of the OP is no POD, since it has a non-trivial constructor. – smerlin Mar 25 '11 at 10:13
  • 1
    -1 as smerlin said, the new definition of POD doesn't fit his requirements at all. – Lightness Races in Orbit Mar 25 '11 at 10:28
  • I fail to see why the C++0x definition is "better" than C++03's one. OP's example is not a POD, but if it has a trivial default constructor **and** a non trivial non default constructor setting `a` to zero, then it is a POD ? The definition sucks as much as the old one. Anyways we are only interested here in *standard-layout classes*, which seems a good concept. – Alexandre C. Mar 25 '11 at 10:33
  • Okay, still not a POD, but is `memcpy` safe? – edA-qa mort-ora-y Mar 25 '11 at 10:35
  • @Alex, I've replaced my 0 with a 123 to ensure I don't match the notion of trivial construction. I don't know if that changes your remark at all, just thought I should mention it. – edA-qa mort-ora-y Mar 25 '11 at 10:38
  • @edA-qa mort-ora-y: This does not change anything (except s/zero/123). Default constructors and non default constructors are handled differently. – Alexandre C. Mar 25 '11 at 10:51
  • 3
    It's not POD, nor on C++0x, but C++0x relaxes the `memcpy` preconditions from POD to simple *trivially copyable*. That's why the C++0x definitions are "better". – Ben Voigt Mar 28 '11 at 04:14
1

One issue with your example is that it has an implicitly-declared, trivial destructor. Despite the name, the implementation is not AFAIK forbidden from doing something in a trivial destructor of a non-POD class.

So legally on some weird implementation, your class ex_struct could exhibit runtime behavior equivalent to the following:

struct weird_ex_struct
{
  int a,b,c,d;
  weird_ex_struct() : a(123), aptr(&a) { }
  weird_ex_struct(const weird_ex_struct &o) : 
    a(o.a), b(o.b), c(o.c), d(o.d), aptr(&a) {}
  weird_ex_struct &operator=(const weird_ex_struct &o) {
    a = o.a; //etc
    aptr = &a;
    return *this;
  }
  ~weird_ex_struct() {
    if (aptr != &a) std::terminate();
  }
private:
  int *aptr;
}

I say runtime behavior, because weird_ex_struct has a non-trivial destructor, and that affects how it can legally be used (not in unions, for one thing). Also I think there are standard ways to detect the existence of private data members at compile-time. But as long as the implementation can keep this stuff secret unless you do something undefined (memcpy a non-POD object), it's then allowed to spring the surprise on you later.

Clearly if weird_ex_struct is copied with memcpy, then something strange will happen when it's destroyed.

There's no obvious reason for an implementation to do this, but the standard left non-POD classes wide open for implementations to do odd things. Not sure whether this is because they thought anyone would think of some useful weirdness, or just because they didn't get around to defining standard-layout like C++0x does.

[Edit: Johannes has pointed that I'm wrong about trivial destructors - for reasons set out in the part of the standard dealing with object lifetime, an implementation can't do things in trivial destructors that rely on the contents of the memory of the object. Possibly they can if the destructor is called explicitly, I'm not certain.

However, the fact remains that the standard permits implementations to do quite a lot of crazy things with non-POD objects, and as soon as you write a constructor, you open that door.]

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Would there be any way, as guaranteed by the standard, of preventing the implementation from doing this? Is C++0x's layout compatibility enough to prevent this? – edA-qa mort-ora-y Mar 25 '11 at 11:09
  • I looked at the draft and updated my post. It appears my struct is a "trivially copyable class" (9.1) but not a trivial class. "trivially copyable classes" may be used in memcpy as they relaxed (3.9.2). Do you read that the same? – edA-qa mort-ora-y Mar 25 '11 at 11:21
  • @edA-qa mort-ora-y: In C++03 no, the definition of POD *is* the way to get `memcpy` working. In C++0x I'm not sure, I'm not familiar enough with it. I just tried to look it all up, but misunderstood something and deleted the comment... But yes, I do agree with your reading that "trivially copyable" is the property you want, and that `ex_struct` is trivially copyable whereas `weird_ex_struct` is not. – Steve Jessop Mar 25 '11 at 11:23
  • 1
    Note though that you can simulate constructors to get what you want using POD classes in some cases: `ex_struct make_ex_struct(int a) { ex_struct foo; foo.a = a; return foo; }`. Then use `make_ex_struct` in some (not all) circumstances where you'd otherwise use the constructor. – Steve Jessop Mar 25 '11 at 11:28
  • The lifetime of an object of type T ends "if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or the storage which the object occupies is reused or released.". Therefor, whatever an implementation might want to do in a trivial destructor, the implementation must not dependent on it (you are also allowed to apply `delete` on a pointer to incomplete class, as long as its destructor is trivial): "if there is no explicit call to the destructor [...] any program that depends on the side effects produced by the destructor has undefined behavior." – Johannes Schaub - litb Mar 26 '11 at 15:18
  • @Johannes: sure, I can't think of any good reason for an implementation to do what I describe in code above. It doesn't *need* to check the pointer, it just does it because it's allowed to. I'm basically applying the "hostile worst case" principle, the only practical reason I can think of to do anything like this is in a debugging mode, to help catch programs that break the rules. – Steve Jessop Mar 26 '11 at 15:53
  • @Steve see 3.8p8 which effectively forbids such a pointer check: You are allowed to mess with the object's memory and even reuse it for a new object, even when there would be an implicit destructor call at the end of its scope, as long as the destructor is trivial. – Johannes Schaub - litb Mar 26 '11 at 16:05
  • @Johannes: OK, so unless the program explicitly calls the destructor (and I expect the questioner's doesn't), I need a different example. Can access to a data member of this class be indirect, as would be the case accessing a virtual base class member? Probably opportunities for that to break after a `memcpy`. Or maybe explore the consequences of 1.8/5: "An object of POD type shall occupy contiguous bytes of storage". I've never really thought about what happens when objects occupy non-contiguous storage, but you'd certainly think it might interfere with `memcpy` copying... – Steve Jessop Mar 26 '11 at 16:24
0

Yes it's safe to copy with memcpy because you constructor only initialize values.

cprogrammer
  • 5,503
  • 3
  • 36
  • 56