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

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]

  1. 除非如下所述,在需要常量表达式的上下文中使用的表达式,若任何子表达式是值依赖的,则该表达式是值依赖的。
  2. 标识符表达式是值依赖的,如果:
    (2.1) — 它是概念id且其任何参数是依赖的,
    (2.2) — 它是类型依赖的,
    (2.3) — 它是非类型模板参数的名称,
    (2.4) — 它命名当前实例化的依赖静态数据成员且未在成员声明器中初始化,
    (2.5) — 它命名当前实例化的依赖静态成员函数,或
    (2.6) — 它命名潜在常量变量(7.7)且其初始化表达式是值依赖的。
    如下形式的表达式是值依赖的,如果一元表达式或表达式是类型依赖的,或类型id是依赖的:
    sizeof unary-expression
    sizeof ( type-id )
    typeid ( expression )
    typeid ( type-id )
    alignof ( type-id )
    noexcept ( expression )
    [注1:关于标准库宏offsetof,参见17.2。— 结束注]
  3. 如下形式的表达式是值依赖的,如果类型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
  4. 如下形式的表达式是值依赖的:
    sizeof ... ( identifier )
    折叠表达式
  5. 形式为&限定id的表达式,若限定id命名当前实例化的依赖成员,则是值依赖的。形式为&转换表达式的表达式,若将转换表达式作为核心常量表达式(7.7)求值成功且求值结果引用模板化实体(具有静态或线程存储期的对象或成员函数),则也是值依赖的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:36:07