Instantiation of a list with an incomplete type in a typedef

clang, libc++, templates, typedef

Solution

If you want guaranteed support for incomplete types, your best bet is to create `unique_ptr`'s to them:

typedef std::list<std::unique_ptr<NodeEntry>> NodeList;
typedef std::list<std::unique_ptr<EdgeEntry>> EdgeList;

In the past, many times `std::list<incomplete_type>` would just work. However with C++11 and `noexcept` specifications, it is becoming more likely that a complete type is needed, just so that the `noexcept` spec can be validated.

C++11 guarantees that `unique_ptr<incomplete_type>` and `shared_ptr<incomplete_type>` will work, although there are strict limits. For example wherever `~unique_ptr()` is executed, the type has to be complete there. But you can usually outline such code to a source and #include the complete type at that point.

`unique_ptr<incomplete_type>` and `shared_ptr<incomplete_type>` are the only class templates in the C++11 std::lib that are guaranteed to work with incomplete types. Everything else is undefined behavior:

[res.on.functions]/p2/b5:

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.

If for some reason the `std::list` does not need to own the pointer to the incomplete type, then `std::list<NodeEntry*>` would work even better. You might also want to entertain using `vector` instead of `list` since the cost of moving pointers (or even `unique_ptr`'s) around is relatively small.

Problem

I have a problem with compiling llvm. The problem is that my current compiler (clang + libc++) tries to instantiate a template before the template parameter gets defined. Here is the code example: ``` // ----- TYPEDEFS ----- class NodeEntry; class EdgeEntry; typedef std::list<NodeEntry> NodeList; typedef std::list<EdgeEntry> EdgeList; typedef NodeList::iterator NodeItr; // line 39 typedef NodeList::const_iterator ConstNodeItr; typedef EdgeList::iterator EdgeItr; typedef EdgeList::const_iterator ConstEdgeItr; typedef std::list<EdgeItr> AdjEdgeList; typedef AdjEdgeList::iterator AdjEdgeItr; class NodeEntry { private: AdjEdgeList adjEdges; ... }; class EdgeEntry { private: AdjEdgeItr node1AEItr, node2AEItr; ... }; ``` The error from the compiler is this: ``` error: field has incomplete type 'PBQP::Graph::NodeEntry' /Developer/Extras/llvm/include/llvm/CodeGen/PBQP/Graph.h:39:13: note: in instantiation of template class 'std::__1::list<PBQP::Graph::NodeEntry, std::__1::allocator<PBQP::Graph::NodeEntry> >' requested here typedef NodeList::iterator NodeItr; ^ /Developer/Extras/llvm/include/llvm/CodeGen/PBQP/Graph.h:31:11: note: forward declaration of 'PBQP::Graph::NodeEntry' class NodeEntry; ``` As far as I can tell the compiler tries to instantiate `std::list<NodeEntry>` in order to get the iterator. This fails as NodeEntry is not defined yet. And of course EdgeEntry is using NodeEntry and vice versa. The obvious question is: How do I fix it? The educational question is: Why does the compiler try to instantiate the template when defining the type? Should it not wait until we do something with the list? Thanks.

Original source

Related problems