'Which STL data structures with an incomplete type stored can be used as a class member?

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.?



Solution 1:[1]

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.

Solution 2:[2]

since C++17 some STL data structures may "exist" with an incomplete type as the template parameter which describes the type stored.

This is incorrect.

Since C++17, some STL types may be declared with an incomplete type as the template parameter.

By the time the types are instantiated, the types must be complete.

For example: (untested code)

struct T; // incomplete
using TV = std::vector<T>; // declared a type using incomplete type T; fine.

TV tv0; // attempt to declare a variable of type TV; fails to compile.

struct T { int v; }; // T is now complete
TV tv1; // compiles

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Marshall Clow