Я пытался создать двунаправленную древовидную структуру, поэтому я ve ended up with the following struct`s:
template<typename T>
struct Node
{
T value;
std::vector<Node<T>> kids;
boost::optional<Node<T>> parent { boost::none };
};
template<typename T>
struct Tree
{
std::vector<Node<T>> heads;
};
Но когда я попытался использовать такие Tree/Node, как, например, значение в unordered_map, я получил ошибку компиляции (MSVC 17.0, C++ 14):
...\boost\include\boost/optional/detail/optional_aligned_storage.hpp(31): error C2027: use of undefined type "Node<std::shared_ptr<Action>>"
После некоторого гугления и запроса Chat-GPT было предложено заменить
boost::optional<Node<T>> parent { boost::none };
с
boost::optional<std::reference_wrapper<Node<T>>> parent { boost::none };
Это сработало. Однако я, честно говоря, понятия не имею, почему именно. Кто-нибудь может это объяснить?
Минимальный воспроизводимый пример на godbolt: нажмите;
Node<T>* parent = nullptr; может быть альтернативой.
Часто для этого вы просто используете указатели. Обычно вы также можете использовать nullptr вместо optional (также рассмотрите std::optional вместо boost — я часто использую boost, но только с вещами, которых еще нет в C++). Кроме того, имейте в виду, что использование ссылок на другие узлы вместо реальных узлов означает, что вам придется позаботиться об очистке - я не знаю, как ваша структура должна работать, поэтому, возможно, здесь будет полезно std::unique_ptr, так как это также очистит родительский узел, как это сделал бы встроенный узел.
Здесь не нужен boost::optional, используйте необработанный указатель.





Кто-нибудь может это объяснить?
boost::optional требуется полный тип. Node еще не завершен, когда вы пытаетесь определить parent с его помощью (оно будет завершено после ; в конце определения структуры).
Причина boost::optional<T> требует полного типа в том, что он удерживает T внутри себя. В вашем случае для этого потребуется Node удерживать Node внутри себя, что невозможно.
Дополнительную информацию о полных и неполных типах можно увидеть здесь: Когда возникает ошибка неполного типа в C++
Использование std::reference_wrapper решает проблему, поскольку создает std::reference_wrapper<Node<T>> объекты, похожие на указатели. Такие объекты не требуют указателя полного типа (грубо говоря, потому, что все указатели одинаковы, независимо от того, на что они указывают).
Вместо boost::optional и std::reference_wrapper вы можете просто использовать необработанный указатель и получить:
Node<T> * parent { nullptr };
Вы можете использовать nullptr как эквивалент пустого boost::optional.
@Evg, хорошо сказано. Я добавил это в ответ.
Не знал, что для boost::optional требуется полный тип. Спасибо!
boost::optionalтребует полного типа, и в этот моментNodeне является полным.std::reference_wrapperсделайте его объектом, похожим на указатель, который не требует полного типа.