C++递归类型定义中sizeof不完全类型错误的原因与排序逻辑
递归变体类型的编译错误原因与定义顺序逻辑
问题重现
基础类型定义
typedef bool Bool; typedef std::monostate Empty; template <typename... Ts> using TupleT = std::tuple<Ts...>; template <typename... Types> struct VariantT { static_assert(sizeof...(Types) > 0, "VariantT must have at least one type"); std::uint8_t tag; std::aligned_union_t<0, Types...> value; };
报错的定义顺序
struct ConsBool; struct ConsListBool; struct Nil; typedef VariantT<ConsBool, Nil> ListBool; typedef VariantT<ConsListBool, Nil> ListListBool; struct ConsBool { using type = TupleT<Bool, ListBool *>; type value; }; struct ConsListBool { using type = TupleT<ListBool, ListListBool *>; type value; // <- 此行触发错误 }; struct Nil { Empty value; };
编译时触发错误:
/usr/include/c++/12/type_traits: In instantiation of ‘const std::size_t std::__strictest_alignment<Nil>::_S_size’: /usr/include/c++/12/type_traits:2136:57: required from ‘const std::size_t std::__strictest_alignment<ConsBool, Nil>::_S_size’ /usr/include/c++/12/type_traits:2157:56: required from ‘const std::size_t std::aligned_union<0, ConsBool, Nil>::_S_len’ /usr/include/c++/12/type_traits:2163:71: required from ‘struct std::aligned_union<0, ConsBool, Nil>’ /usr/include/c++/12/type_traits:2606:11: required by substitution of ‘template<long unsigned int _Len, class ... _Types> using aligned_union_t = typename std::aligned_union::type [with long unsigned int _Len = 0; _Types = {ConsBool, Nil}]’ include/types/compound.hpp:19:39: required from ‘struct VariantT<ConsBool, Nil>’ /usr/include/c++/12/tuple:69:23: required from ‘struct std::_Tuple_impl<0, VariantT<ConsBool, Nil>, VariantT<ConsListBool, Nil>*>’ /usr/include/c++/12/tuple:981:11: required from ‘class std::tuple<VariantT<ConsBool, Nil>, VariantT<ConsListBool, Nil>*>’ include/main/main.hpp: required from here /usr/include/c++/12/type_traits:2136:9: error: invalid application of ‘sizeof’ to incomplete type ‘Nil’ 2136 | sizeof(_Tp) > __strictest_alignment<_Types...>::_S_size
编译通过的定义顺序
struct Nil; struct ConsBool; struct ConsListBool; typedef VariantT<ConsBool, Nil> ListBool; typedef VariantT<ConsListBool, Nil> ListListBool; struct Nil { Empty value; }; struct ConsBool { using type = TupleT<Bool, ListBool *>; type value; }; struct ConsListBool { using type = TupleT<ListBool, ListListBool *>; type value; };
错误原因分析
核心问题在于std::aligned_union_t的工作机制:它需要计算所有传入类型的大小和最大对齐要求,以此确定内存块的尺寸。而计算类型大小必须依赖完整类型——即已经完成定义的结构体/类,仅前向声明的不完整类型无法使用sizeof运算符。
在报错的定义顺序中:
当编译器处理ConsListBool的type value;时,需要实例化std::tuple<ListBool, ListListBool*>。其中ListBool是VariantT<ConsBool, Nil>,而VariantT的成员std::aligned_union_t<0, ConsBool, Nil> value需要计算Nil的大小,但此时Nil还只是前向声明(完整定义在ConsListBool之后),属于不完整类型,因此触发sizeof应用于不完整类型的错误。
结构体定义顺序的逻辑
C++中,类型的使用场景决定了是否需要完整类型:
- 仅声明指针/引用时,前向声明的不完整类型足够;
- 当需要计算类型大小、访问成员、实例化对象时,必须使用完整类型。
编译通过的顺序严格遵循了这个规则:
- 先对所有类型做前向声明,满足
typedef阶段对类型名称的需求; - 优先完成
Nil的完整定义,确保后续实例化VariantT<ConsBool, Nil>时,Nil是完整类型,可以计算大小; - 再定义
ConsBool和ConsListBool,此时ListBool的大小能被正确计算,TupleT的实例化不会遇到不完整类型的问题。
简言之,所有被用于计算大小的类型,必须在需要计算其大小的代码之前完成完整定义。
内容的提问来源于stack exchange,提问作者George Ogden
相关产品推荐
相关产品推荐

