I was trying to create a bidirectional tree-like structure, so Ive 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;
};
But when I tried to use such Tree
/Node
as, for instance, value in unordered_map
, I got compilation error (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>>"
After some googling and asking Chat-GPT, it was suggested to replace
boost::optional<Node<T>> parent { boost::none };
with
boost::optional<std::reference_wrapper<Node<T>>> parent { boost::none };
That worked. However, I honestly have no idea why exactly. Can anyone explaing that?
Minimal reproducible example on godbolt: click;
Can anyone explaing that?
boost::optional
requires a complete type. Node
is not yet complete when you attempt to define parent
using it (it will be complete after ;
at the end of the struct definition).
The reason boost::optional<T>
requires a complete type is that it holds T
inside itself. In your case this would require Node
to hold a Node
inside itself which is impossible.
You can see some more info about complete and incomplete types here: When does an Incomplete Type error occur in C++
Using std::reference_wrapper
solves the problem because it makes std::reference_wrapper<Node<T>>
a pointer-like objects. Such objects do not require the pointee to the a complete type (roughly speaking it's because all pointers are similar, no matter what they point to).
Intead of boost::optional
and std::reference_wrapper
you can simply use a raw pointer and have:
Node<T> * parent { nullptr };
You can use nullptr
as an equivalent to an empty boost::optional
.