我在编译llvm时遇到问题。问题是我的当前编译器(clang + libc ++)尝试在定义模板参数之前实例化模板。这是代码示例:
// ----- 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;
...
};
编译器的错误是:
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;
据我所知,编译器尝试实例化std::list<NodeEntry>
以获取迭代器。由于尚未定义NodeEntry,因此失败。当然,EdgeEntry正在使用NodeEntry,反之亦然。
显而易见的问题是:我该如何解决? 教育问题是:为什么编译器在定义类型时会尝试实例化模板?它应该等到我们对列表做些什么吗?
感谢。
答案 0 :(得分:3)
如果您希望保证支持不完整类型,最好的办法是为他们创建unique_ptr
:
typedef std::list<std::unique_ptr<NodeEntry>> NodeList;
typedef std::list<std::unique_ptr<EdgeEntry>> EdgeList;
在过去,std::list<incomplete_type>
很多次都会起作用。但是,对于C ++ 11和noexcept
规范,更有可能需要完整类型,以便可以验证noexcept
规范。
C ++ 11保证unique_ptr<incomplete_type>
和shared_ptr<incomplete_type>
可以正常工作,尽管有严格的限制。例如,只要执行~unique_ptr()
,就必须在那里完成类型。但是,您通常可以将此类代码概述为源代码,并在此时#include完整类型。
unique_ptr<incomplete_type>
和shared_ptr<incomplete_type>
是C ++ 11 std :: lib中唯一允许使用不完整类型的类模板。其他一切都是未定义的行为:
[res.on.functions] / P2 / B5:
特别是,在以下情况下效果未定义:
...
- 如果在实例化模板组件时将不完整类型(3.9)用作模板参数,除非特别允许该组件。
如果由于某种原因,std::list
不需要拥有指向不完整类型的指针,那么std::list<NodeEntry*>
将更好地工作。您可能还想使用vector
代替list
来娱乐,因为移动指针(甚至unique_ptr
)的成本相对较小。
答案 1 :(得分:2)
根据已经链接的clang docs,他们不愿意支持libc ++中stl容器的不完整类型。
由此引发的一些有趣的事情是以下代码无法使用libc ++进行编译:
#include <list>
struct Tree {
// ... more stuff ...
std::list<Tree> mChildren;
};
但是这段代码编译得很好,因为list的模板参数也取决于模板参数:
template<typename T>
struct TreeT {
// ... more stuff ...
private:
std::list<TreeT<T> > mChildren;
};
这让我很奇怪,因为后者更复杂。
在类似的post上,它还包含对ISO部分的关于模板中不完整类型的引用,Boost.Container被提及作为替代,因为它明确允许递归数据结构。我在诊断类似问题时遇到了这个帖子,这是我现在的解决方案。