As far as I know, since C++17 some STL data structures may "exist" with an incomplete type as the template parameter which describes the type stored. For example, I may use std::unique_ptr<Incomplete>
(I'm not sure if it's a data structure though) or std::vector<Incomplete>
as class members if all properties of the class (which need Incomplete
's definition) are implemented in a separate .cpp file:
class Incomplete;
using Complete = int;
class Foo {
private:
std::unique_ptr<Incomplete> u_p;
std::vector<Incomplete> v;
std::deque<Incomplete> d;
std::list<Incomplete> l;
std::set<Incomplete> s;
std::unordered_map<Complete, Complete> u_m_cc;
std::unordered_map<Complete, Incomplete> u_m_ci;
std::unordered_map<Incomplete, Complete> u_m_ic;
std::unordered_map<Incomplete, Incomplete> u_m_ii;
public:
// implemented in a separate .cpp which has Incomplete defined:
Foo();
Foo(Foo&&);
Foo& operator=(Foo&&);
Foo(Foo const&);
Foo& operator=(Foo const&);
~Foo();
};
So, which of the data members listed above are valid for such usage? What about other data structures, smart pointers etc.?
Assuming none of the classes members are used explicitly or implicitly until the type is complete:
The template argument can always be incomplete for std::unique_ptr
and std::shared_ptr
since C++11, see [unique.ptr]/5 and [util.smartptr.shared]/2 respectively.
Support of incomplete types in containers was added with N4510 to C++17, but only for
std::vector
std::list
std::forward_list
and only if the allocator used fulfills the allocator completeness requirements, namely that, even if the value type itself is not complete, the allocator type X
itself is a complete type and so are all members of std::allocator_traits<X>
, except ::value_type
. The default allocator std::allocator
fulfills these requirements.
None of the other containers can be used with incomplete types. According to the proposal linked above the scope was limited to these three containers "as a first step" because the major implementations already had support for it.