非虚派生类与首个基类无指针可转换性的标准制定原因
C++标准禁止非指针可互转换基类直接从void* static_cast的底层原因
核心原因:预留实现灵活性 + 维护类型系统严谨性
1. 实现有权选择非零偏移的布局
虽然主流x86/ARM等平台的编译器,会把无虚函数、无虚继承、无多重继承的单一公有基类子对象放在派生类的偏移0位置,但C++标准从未强制要求这一点。
标准仅规定:标准布局类的首个非静态成员与对象本身具备指针可互转换性;但对于基类,哪怕是单一公有非虚基类,标准并没有将其纳入指针可互转换的范畴。这是为特殊架构的实现预留了灵活性——比如某些嵌入式硬件、古老的非冯诺依曼架构,可能因为内存对齐要求、地址空间限制或需要额外元数据,将基类子对象放在非零偏移处。如果标准允许直接从void*转换到基类指针,这类实现的正确性会被破坏。
2. 维护类型转换规则的明确性
指针可互转换性是C++类型系统中一套清晰的安全转换规则,定义了哪些类型间可以直接通过reinterpret_cast或经void*的static_cast完成转换。如果放宽规则允许单一公有基类与派生类直接互转,会模糊规则边界:比如后续派生类添加其他基类、或者实现调整布局时,原本“正常”的转换会立刻变成未定义行为。
标准通过明确限制指针可互转换的场景,避免代码依赖未标准化的实现细节,保证代码行为的可预测性。
对示例的补充说明
你给出的代码在x86-64 GCC下编译出完全相同的汇编,这只是特定实现的行为,而非标准保证的结果。std::is_pointer_interconvertible_base_of_v<Base, Derived>返回false,正是因为标准不将这种单一公有非虚基类的情况视为指针可互转换——哪怕实际布局上两者指针值相同。
关于布局的两个具体问题
- C++实现允许将
Base放在Derived的非零偏移处,标准对此没有禁止。 - 目前确实存在这类实现:部分针对特殊嵌入式硬件的编译器、或兼容旧内存模型的小众编译器会采用这种布局,但主流通用平台(GCC、Clang、MSVC等)的编译器均使用基类子对象偏移0的布局。
内容的提问来源于stack exchange,提问作者Jonathan S.
相关产品推荐
相关产品推荐

