解决boost::optional未定义类型错误:为何改用reference_wrapper有效?
问题背景
我尝试创建一个双向树形结构,编写了如下模板结构体:
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>>"
将代码中的boost::optional<Node<T>> parent { boost::none };替换为boost::optional<std::reference_wrapper<Node<T>>> parent { boost::none };后问题解决,想知道具体原因。
原因解析
1. 原始代码的核心问题:不完全类型限制
Node<T>结构体内部包含boost::optional<Node<T>>成员,此时编译器处理Node<T>定义时,Node<T>本身属于不完全类型——整个结构体还未完成定义,编译器无法确定它的大小和内存布局。
而在C++14标准下,Boost.Optional要求存储的类型必须是完整类型:因为它需要为存储的对象分配对齐的存储空间,不完全类型的大小无法确定,编译器无法完成这个操作。当unordered_map实例化相关构造、赋值函数时,会触发boost::optional<Node<T>>的完整实例化,此时编译器检测到Node<T>仍为不完全类型,就抛出了“使用未定义类型”的错误。
2. std::reference_wrapper的解决逻辑
std::reference_wrapper<Node<T>>本质是对Node<T>对象引用的包装,它的大小固定(等同于指针大小),定义时仅需知道Node<T>是一个合法类型即可,不需要依赖其完整的内存布局。
此时boost::optional存储的是reference_wrapper而非Node<T>本身:
- 在定义
Node<T>结构体阶段,std::reference_wrapper<Node<T>>仅需依赖Node<T>的不完全类型,编译器可以正常处理。 - 后续实际使用父节点引用时,
Node<T>已经完成定义,成为完整类型,不会再触发编译错误。
另外从语义上看,双向树形结构的父节点本就应该是引用或指针——既避免复制整个父节点对象的开销,也能防止循环引用带来的对象销毁问题,reference_wrapper正好契合这一需求。
内容的提问来源于stack exchange,提问作者Anton Grant

