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

若采纳P1839,能否通过对象表示偏移访问C++子对象?

基于C++提案P1839的子对象偏移访问合法性分析

这是Stack Overflow问题《通过封闭对象地址加字节偏移访问子对象是否属于未定义行为?》的扩展讨论,前提是假设C++提案P1839已被采纳。

struct V2
{
    float x;
    float y;

    float& f()
    {
        // 如果有影响,可以用char*或unsigned char*替代std::byte
        std::byte* self = reinterpret_cast<std::byte*>(this); // (1)
        std::byte* rep_y = self + offsetof(V2, y); // (2)
        float* ptr_y = reinterpret_cast<float*>(rep_y); // (3)
        return *ptr_y; // (4)
    }
};

此前针对这类代码合法性的讨论围绕C++17之后的对象表示检查展开,共识是形式上(2)中的指针算术属于未定义行为(UB)。而P1839的核心目标正是修复对象表示的迭代问题,理论上能让(2)不再是UB,但需要明确嵌套和子对象相关的规范细节,以下针对几个核心问题逐一分析:

1. 无偏移时的reinterpret_cast合法性

是的,指针可互换性规则中“一方是另一方的对象表示或其首元素”的条款,允许在this(V2*)、它的对象表示指针(比如std::byte*)、以及转回的V2*之间进行reinterpret_cast转换,并且支持后续的值访问。

这是因为对象的首字节指针本身就属于对象表示的一部分,符合指针可互换的条件,转换后访问原对象的行为是合法的。

2. rep_y能否视为y的对象表示首元素?

可以。V2的对象表示包含其所有非静态数据成员的对象表示,y作为V2的直接子对象,它的对象表示首元素就是V2对象表示中偏移offsetof(V2, y)的位置。

哪怕这个指针是从V2的对象表示构造出来的,而非直接取&y,只要偏移计算正确(这里用offsetof保证了这一点),rep_y就确实指向y的对象表示首字节,因此通过(3)转换为float*后访问y的值是合法的。

P1839修复了原标准中对象表示迭代的限制,允许在完整对象的对象表示范围内进行指针算术,只要目标位置属于该对象或其子对象的对象表示,所以这里的操作完全符合提案的意图。

3. 如果存在疑问,std::launder能否解决问题?

即使退一步说,假设ptr_y的合法性存在争议,P1839对std::launder的修改也能解决问题。修改后的std::launder允许清洗指向“原始指针可达范围内的对象”的指针,这里y是this指向的V2对象的直接子对象,显然属于可达范围。

不过在这个场景下其实不需要std::launder——因为前面已经确定rep_y就是y的对象表示首元素,转换后的ptr_y本身就是指向y的有效指针,直接访问没有问题。

4. 若以上都不成立,是否有合法替代方案?

在P1839的框架下,上述代码本身就是合法的,完全符合提案修复对象表示迭代问题的精神。如果要更直接的写法,也可以用unsigned char*替代std::byte*(两者在对象表示操作上等价),或者直接合并转换步骤:

float* ptr_y = reinterpret_cast<float*>(reinterpret_cast<std::byte*>(this) + offsetof(V2, y));

本质逻辑完全一致,都是合法的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 21:35:20