Search code examples
c++templatesstlincomplete-type

Can standard container templates be instantiated with incomplete types?


Sometimes it's useful to instantiate a standard container with an incomplete type to obtain a recursive structure:

struct multi_tree_node { // Does work in most implementations
    std::vector< multi_tree_node > child;
};

struct trie_node { // Does not work in most implementations
    std::map< char, trie_node > next;
};

This tends to work because containers don't have members of type value_type or member functions that pass or return any value_type objects by value. The Standard doesn't seem to say very much about incomplete template arguments, but there is one bit under C++11 §17.6.4.8 [lib.res.on.functions], "requirements on other functions":

In particular, the effects are undefined in the following cases: … if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

Does this make the above constructs illegal, even though the instantiations are not in block scope? Does this fall under "operations on types used to instantiate standard library template components" (also 17.6.4.8)? Or is a library implementation forbidden to incur template instantiations that might fail for incomplete types when all specifically required instantiations succeed?

Edit: Since only functions may call and instantiate other functions, restricting "operations on types…" to those in block scope would seem to hold the contents of member functions to a stricter requirement than the contents of signatures and member class definitions. After all, it certainly doesn't make sense to do anything with a multi_tree_node until the type is complete. And this extends to the std::unique_ptr which explicitly supports an incomplete type argument, even when used in block scope.

Edit 2: Serves me right for not bothering to test the trie_node example — and I've even tried it before. It's the same as the example of breakage in the article that @Ise linked. However, while the article seems to take for granted that "nothing like that could work," the solution seems simple to me — std::map's internal tree_node class should be a non-member template, not a member non-template class.

Anyway, that article establishes design intent pretty well, so I guess my nitpick about being under the subheading of "requirements on functions" is only just that.


Solution

  • Personally, I feel the wording instantiating in 17.6.4.8/2 is a little ambiguous, but according to this article, the standard's intent seems not to allow recursive data type using standard containers.

    On a related note, VC2005 issues an error for class C { std::deque< C > x; };, while it compiles class C { std::vector< C > x; };...
    However, in my understanding, this restriction is just for expanding the freedom of the implementation of standard containers. So as Kerrek SB mentioned, there can be containers which allow recursive data structure, and Boost.Container seems to provide this facility.