2012-04-08 69 views
3

编译llvm时遇到问题。问题是我的当前编译器(clang + libC++)试图在template参数被定义之前实例化一个模板。下面是代码示例:在typedef中使用不完整类型的列表实例化

// ----- 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,反之亦然。

显而易见的问题是:我该如何解决?
教育问题是:为什么编译器在定义类型时尝试实例化模板?它是否应该等到我们对清单做些什么?

谢谢。

+1

它似乎可以在linux上用clang ++ 3.0和libC++(svn 154095)编译。 – alexisdm 2012-04-08 20:09:38

+0

有趣。根据铿锵网站[该功能不支持](http://clang.llvm.org/compatibility.html#undep_incomplete) – 2012-04-09 10:17:14

回答

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.功能]/P2/B5:

特别地,该效果未定义在下列情况下:

...

  • 如果一个不完整的类型(3.9)被用作模板实例化模板组件时的参数,除非该组件特别允许。

如果由于某种原因,std::list并不需要自己的指针不完整的类型,然后std::list<NodeEntry*>将工作做得更好。您可能还想使用vector而不是list进行娱乐,因为移动指针(或甚至是unique_ptr)的成本相对较小。

2

根据clang docs也已经链接,他们不愿意支持libC++中stl容器的不完整类型。

一些有趣的即源于此被下面的代码不会的libC++编译:

#include <list> 

struct Tree { 
    // ... more stuff ... 
    std::list<Tree> mChildren; 
}; 

但是这个代码编译好,因为表的模板参数还取决于模板参数:

template<typename T> 
struct TreeT { 
    // ... more stuff ... 
private: 
    std::list<TreeT<T> > mChildren; 
}; 

这让我觉得很奇怪,因为后者更复杂。

对于类似的post,其中还包含对涉及模板中不完整类型的ISO部分的引用,Boost.Container被提及为替代方法,因为它明确允许递归数据结构。我在诊断类似问题时遇到了这篇文章,这是我现在的解决方案。