Создание экземпляра списка с неполным типом в typedef
У меня проблема с компиляцией 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 ответа:
Если вы хотите получить гарантированную поддержку неполных типов, лучше всего создать
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()
, тип должен быть завершен. Но обычно вы можете набросать такой код в исходном коде и #включить полный тип в этот момент.
unique_ptr<incomplete_type>
иshared_ptr<incomplete_type>
являются единственными шаблонами классов в C++11 std:: lib, которые гарантированно работают с неполными типами. Все остальное-неопределенное поведение:[ОТВ.функции] / p2/b5:
В частности, эффекты не определены в следующих случаях:...
- если в качестве аргумента шаблона используется неполный тип (3.9 при создании экземпляра компонента шаблона, если специально не разрешено для этого компонента.
Если по какой-то причине
std::list
не нужно владеть указателем на неполный тип, тоstd::list<NodeEntry*>
будет работать еще лучше. Вы также можете использоватьvector
вместоlist
, поскольку стоимость перемещения указателей (или дажеunique_ptr
) относительно невелика.
Согласно Clang docs, также уже связанным, они не готовы поддерживать неполные типы для контейнеров stl в libc++.
Что-то забавное, что вытекает из этого, следующий код не будет компилироваться с 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.Контейнер упоминается в качестве альтернативы, поскольку он явно допускает рекурсивные структуры данных. Я наткнулся на этот пост, диагностируя подобную проблему, и это мое решение на данный момент.