【问题标题】:Instantiation of a list with an incomplete type in a typedef在 typedef 中实例化具有不完整类型的列表
【发布时间】:2012-04-08 18:45:08
【问题描述】:

我在编译 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&lt;NodeEntry&gt; 以获取迭代器。这失败了,因为尚未定义 NodeEntry。当然,EdgeEntry 使用的是 NodeEntry,反之亦然。

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

谢谢。

【问题讨论】:

  • 在 linux 上使用 clang++ 3.0 和 libc++(svn 版本 154095)似乎可以正常编译。
  • 有趣。根据clang网站the feature is not supported

标签: templates typedef clang libc++


【解决方案1】:

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

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

在过去,很多时候std::list&lt;incomplete_type&gt; 会起作用。然而,对于 C++11 和 noexcept 规范,越来越有可能需要一个完整的类型,以便可以验证 noexcept 规范。

C++11 保证 unique_ptr&lt;incomplete_type&gt;shared_ptr&lt;incomplete_type&gt; 可以工作,尽管有严格的限制。例如,无论在哪里执行~unique_ptr(),类型都必须是完整的。但是您通常可以将此类代码概述到一个源代码中,然后在此时#include 完整的类型。

unique_ptr&lt;incomplete_type&gt;shared_ptr&lt;incomplete_type&gt; 是 C++11 std::lib 中唯一保证可以处理不完整类型的类模板。其他一切都是未定义的行为:

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

特别是在以下情况下效果是不确定的:

...

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

如果由于某种原因std::list 不需要拥有指向不完整类型的指针,那么std::list&lt;NodeEntry*&gt; 会更好地工作。您可能还想使用vector 而不是list 来娱乐,因为移动指针(甚至unique_ptr's)的成本相对较小。

【讨论】:

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

    【讨论】:

      猜你喜欢
      • 2012-11-18
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      • 2017-11-21
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 2017-06-21
      相关资源
      最近更新 更多