Создание экземпляра списка с неполным типом в 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 3

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.Контейнер упоминается в качестве альтернативы, поскольку он явно допускает рекурсивные структуры данных. Я наткнулся на этот пост, диагностируя подобную проблему, и это мое решение на данный момент.