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

C++11中依赖std::addressof非活跃union成员的constexpr offsetof实现合规吗?

C++11中constexpr版offsetof实现的标准合规性分析

先直接给结论:你提供的这个实现在C++11标准中属于未定义行为,问题的核心就在于对union的非活跃成员调用std::addressof这一操作违反了标准规定。

先把你的代码片段整理出来方便分析:

#include <iostream>
#include <cstdint>
#include <memory>

template <typename T, typename M>
constexpr std::uintptr_t constexpr_offsetof(M T::*member) noexcept {
    union {
        T obj;
        char dummy;
    } u;
    return reinterpret_cast<std::uintptr_t>(std::addressof(u.obj.*member)) -
           reinterpret_cast<std::uintptr_t>(std::addressof(u.obj));
}

struct Test {
    int a;
    double b;
};

int main() {
    std::cout << constexpr_offsetof(&Test::a) << std::endl;
    std::cout << constexpr_offsetof(&Test::b) << std::endl;
    return 0;
}

为什么这是未定义行为?

咱们结合C++11的标准条款来拆解:

  1. Union的活跃成员规则

根据C++11标准的[class.union]章节:一个union在任意时刻只能有一个活跃成员。当你对union的某个成员执行初始化或赋值操作时,该成员成为活跃成员,其余成员则变为非活跃状态。

在你的代码里,union u是默认初始化的——既没有显式初始化obj,也没有初始化dummy,所以u.obj从始至终都是非活跃成员。

  1. 访问非活跃union成员的限制
    根据C++11标准的[expr.ref]章节(成员访问表达式规则):如果成员访问的目标是union的非活跃成员,那么这个表达式的行为是未定义的。

你代码里的u.obj.*member本质是访问非活跃成员u.obj的子对象,哪怕只是用std::addressof取它的地址,这个操作本身已经违反了标准要求,属于未定义行为。

这里要注意:std::addressof只是保证返回对象的真实地址,但它不能“合法化”一个本身就违规的对象访问——如果访问非活跃union成员是标准禁止的,那不管用不用std::addressof,整个操作都不符合标准。

为什么GCC和Clang能编译通过?

这只是编译器的扩展行为而已。很多编译器为了方便某些场景(比如这类offsetof的实现),放宽了对union非活跃成员访问的限制,但这并不是C++11标准要求的行为。换个严格遵循标准的编译器,或者开启更严格的编译选项,这个代码可能就会报错或者出现不可预测的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:40:26