2012-04-08 68 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被提及爲替代方法,因爲它明確允許遞歸數據結構。我在診斷類似問題時遇到了這篇文章,這是我現在的解決方案。