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 th
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.
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.]
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]
.
Yes it's safe to copy with memcpy because you constructor only initialize values.