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的标准条款来拆解:
- Union的活跃成员规则
根据C++11标准的[class.union]章节:一个union在任意时刻只能有一个活跃成员。当你对union的某个成员执行初始化或赋值操作时,该成员成为活跃成员,其余成员则变为非活跃状态。
在你的代码里,union u是默认初始化的——既没有显式初始化obj,也没有初始化dummy,所以u.obj从始至终都是非活跃成员。
- 访问非活跃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
相关产品推荐
相关产品推荐

