g++中offsetof使用运行时值编译失败是否为回归问题?
offsetof宏使用运行时值的编译器行为分歧与标准解析
问题场景
示例代码:
#include <cstddef> struct type { int array[4]; }; int main(int argc, char**) { return offsetof(type, array[argc]); }
编译表现:
- g++ 11.1及后续版本编译报错,错误信息:
<source>: In function 'int main(int, char**)': <source>:8:33: error: 'argc' is not a constant expression 8 | return offsetof(type, array[argc]); | ^~~~
- 旧版g++(11.1之前)、最新版clang++、MSVC、icx均可正常编译并执行预期行为。
核心问题:g++的行为是否正确?已有已关闭的GCC Bug报告称其行为正确,但未提及值依赖相关内容。
额外需求:解释“type依赖但offsetof表达式不依赖类型”的含义并举例说明。
一、g++的行为是否符合标准?
根据C++20标准草案(n4868)的规则,offsetof(type, member-designator)的依赖属性明确为:offsetof表达式永远不依赖类型,仅当type是依赖类型时,表达式的值才依赖。
在示例代码中,type是一个非依赖的具体结构体,因此offsetof(type, array[argc])不属于值依赖表达式。而offsetof本质是编译期计算内存偏移量的宏,标准要求其第二个参数member-designator必须是常量表达式。g++ 11.1及后续版本严格执行了这一要求:argc是运行时变量,不属于常量表达式,因此触发报错。
其他编译器的行为属于放宽检查,允许在offsetof中使用运行时表达式,但这并不符合标准的严格规定。GCC的行为是正确的,与已关闭的GCC Bug报告结论一致。
二、“type依赖但offsetof表达式不依赖类型”的含义及示例
含义拆解
- type依赖:指
offsetof的第一个参数type是依赖模板参数的类型(比如模板参数本身,或包含模板参数的复合类型)。 - offsetof表达式不依赖类型:无论
type是否依赖,offsetof表达式的返回类型始终是std::size_t(标准强制规定),不会随type的依赖属性变化。 - 仅当type依赖时值才依赖:当
type是依赖类型时,offsetof计算出的偏移量值需要等到模板实例化时才能确定,因此表达式的值是依赖的;若type是非依赖类型,偏移量在编译期即可确定,值不依赖。
示例代码
#include <cstddef> template<typename T> struct Wrapper { T data; int extra; }; template<typename T> std::size_t get_extra_offset() { // Wrapper<T>是依赖类型(依赖模板参数T) // offsetof表达式的类型固定为std::size_t(不依赖类型) // 表达式的值依赖于T:不同T的大小会导致extra的偏移量不同 return offsetof(Wrapper<T>, extra); } int main() { // 实例化后确定偏移量:Wrapper<int>中extra的偏移为4(假设int占4字节) auto offset_int = get_extra_offset<int>(); // Wrapper<double>中extra的偏移为8(假设double占8字节) auto offset_double = get_extra_offset<double>(); return 0; }
附录:C++20标准草案(n4868)13.8.3.4 节(值依赖表达式)中文翻译
13.8.3.4 值依赖表达式 [temp.dep.constexpr]
- 除非如下所述,在需要常量表达式的上下文中使用的表达式,若任何子表达式是值依赖的,则该表达式是值依赖的。
- 标识符表达式是值依赖的,如果:
(2.1) — 它是概念id且其任何参数是依赖的,
(2.2) — 它是类型依赖的,
(2.3) — 它是非类型模板参数的名称,
(2.4) — 它命名当前实例化的依赖静态数据成员且未在成员声明器中初始化,
(2.5) — 它命名当前实例化的依赖静态成员函数,或
(2.6) — 它命名潜在常量变量(7.7)且其初始化表达式是值依赖的。
如下形式的表达式是值依赖的,如果一元表达式或表达式是类型依赖的,或类型id是依赖的:sizeof unary-expressionsizeof ( type-id )typeid ( expression )typeid ( type-id )alignof ( type-id )noexcept ( expression )
[注1:关于标准库宏offsetof,参见17.2。— 结束注]- 如下形式的表达式是值依赖的,如果类型id或简单类型说明符是依赖的,或表达式或转换表达式是值依赖的:
simple-type-specifier ( expression-listopt )static_cast < type-id > ( expression )const_cast < type-id > ( expression )reinterpret_cast < type-id > ( expression )( type-id ) cast-expression- 如下形式的表达式是值依赖的:
sizeof ... ( identifier )
折叠表达式- 形式为&限定id的表达式,若限定id命名当前实例化的依赖成员,则是值依赖的。形式为&转换表达式的表达式,若将转换表达式作为核心常量表达式(7.7)求值成功且求值结果引用模板化实体(具有静态或线程存储期的对象或成员函数),则也是值依赖的。
内容的提问来源于stack exchange,提问作者Ted Lyngmo
相关产品推荐
相关产品推荐

