Note: as of C++11, the explanation below is outdated: standard library containers now work with incomplete types.
Why is this?
Because the C++ standard library containers are not defined to work with incomplete member types. This is by design1 – but it’s arguably an error (and might be changed in future versions of C++). The Boost.Containers library fixes this.
Your code with pointers works because a pointer to an incomplete type is itself a complete type. However, this obviously changes the semantics of your type quite drastically (in particular, who manages the memory?) and it’s generally not a good idea to use this as a replacement.
1 It’s worth pointing out that the article claims that you technically cannot implement std::map
to work with incomplete types. However, this claim is wrong.