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

C++23中指针可互转换类型的类型双关是否不受严格别名规则限制?

原代码的合法性分析

你的原代码存在未定义行为,核心问题在于违反了严格别名规则:

  1. 严格别名规则的限制
    根据C++23 [basic.lval]/11,只有当glvalue的类型属于特定允许的集合时,才能用它访问对象的存储值。S<true>和S<false>是不同的模板实例化类型,既不属于彼此的动态类型,也不满足「相似类型」的定义(相似类型仅允许cv限定、指针/成员指针/数组的cv差异等,不包含模板参数值不同的实例)。因此,通过reinterpret_cast<S<false>*>(this)生成的S<false>类型glvalue访问S<true>对象的存储,属于未定义行为。

  2. 指针可互转换的误解
    你提到的「传递性」是错误的:C++标准中没有规定指针可互转换关系具有传递性。S<true>与它的x成员是指针可互转换的,S<false>与它的x成员也是,但这并不意味着S<true>和S<false>的对象之间是指针可互转换的——指针可互转换仅适用于同一对象、标准布局类的首成员/基类子对象、同一联合的成员这些直接场景。


布局兼容类型的意义

布局兼容的核心价值并非允许跨类型的指针访问,而是解决以下场景的定义性问题:

  • 字节复制的合法性:用memcpy等函数在两个布局兼容的标准布局类型对象之间复制字节,结果是定义良好的([class.mem.general]/23)。
  • 联合的跨成员访问:如果联合的两个成员是布局兼容的标准布局类型,那么活跃其中一个成员后,可以安全读取另一个成员的公共初始序列([class.union]/2)。
  • 指针比较的一致性:布局兼容的标准布局类型的指针经reinterpret_cast后,比较结果与原指针的比较结果一致([expr.reinterpret.cast]/7)。

修改后的代码合法性分析

如果将x移到非模板基类中,代码变为:

#include <type_traits>

struct Base { int x; };
template<bool B> struct S : Base {
  void go() {
    ++x;
    if constexpr (B)
      reinterpret_cast<S<false>*>(this)->go();
  }
};
static_assert(std::is_standard_layout_v<S<true>> && std::is_standard_layout_v<S<false>>);

int main() {
  S<true>{}.go();
  return 0;
}

此时代码是合法的,原因如下:

  • S<true>和S<false>仍为布局兼容的标准布局类,且它们的基类Base是完全相同的类型。
  • 当通过reinterpret_cast<S<false>*>(this)访问x时,实际是访问继承自Base的成员,编译器会隐式将S<false>*转换为Base*。根据严格别名规则[basic.lval]/11.6,允许通过基类类型的glvalue访问派生类对象的基类子对象——这里Base是S<true>的基类,glvalue指向的正是对象的Base子对象,完全符合规则要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 06:50:15