You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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++中,类型的使用场景决定了是否需要完整类型:

  • 仅声明指针/引用时,前向声明的不完整类型足够;
  • 当需要计算类型大小、访问成员、实例化对象时,必须使用完整类型。

编译通过的顺序严格遵循了这个规则:

  1. 先对所有类型做前向声明,满足typedef阶段对类型名称的需求;
  2. 优先完成Nil的完整定义,确保后续实例化VariantT<ConsBool, Nil>时,Nil是完整类型,可以计算大小;
  3. 再定义ConsBool和ConsListBool,此时ListBool的大小能被正确计算,TupleT的实例化不会遇到不完整类型的问题。

简言之,所有被用于计算大小的类型,必须在需要计算其大小的代码之前完成完整定义。

内容的提问来源于stack exchange,提问作者George Ogden

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:40:10