3

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

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

谢谢。

4

2 回答 2

3

如果您想保证对不完整类型的支持,最好的办法是unique_ptr为它们创建 's:

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's)的成本相对较小。

于 2012-07-17T00:55:36.323 回答
2

根据已经链接的clang 文档,他们不愿意在 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;
};

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

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

于 2012-07-16T05:02:34.307 回答