在typedef中实例化具有不完整类型的列表

时间:2012-04-08 18:45:08

标签: templates typedef clang libc++

我在编译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,反之亦然。

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

感谢。

2 个答案:

答案 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被提及作为替代,因为它明确允许递归数据结构。我在诊断类似问题时遇到了这个帖子,这是我现在的解决方案。