是什么阻止编译器消除在每个特定上下文中未使用的数据成员?

Igo*_*r G 6 c++ language-lawyer c++17

是否存在函数foo()optimized_foo()下面示例中不等效的情况?

struct Test
{
    int     x;
    int     y;
    int     z;
};

// Some external function. Implementation unknown.
void bar(int& arg);

void foo()
{
    Test      t;
    t.x = 3;
    t.y = 4;
    t.z = 5;
    bar(t.y);
}

void optimized_foo()
{
    int       t_y = 4;
    bar(t_y);
}
Run Code Online (Sandbox Code Playgroud)

只是所有主要的 x86_64 编译器(gcc 10.2、clang 10.0、msvc 19.24)都会在生成的汇编代码中保留初始化t.xt.zfoo()最高优化级别生成的汇编代码。即使这些成员显然没有被使用。他们有理由吗?

我是否正确假设bar(),在获得对结构的一个数据成员的引用后,没有合法的方法来获取对其他成员的指针/引用?标准对此有何评论?

eer*_*ika 5

bar可以通过引用 [expr.unary.op] 获取成员的地址。然后该函数可以复制相邻成员的对象表示的字节。

void bar(int& arg) {
    constexpr auto size      = sizeof(Test);
    constexpr auto offset    = offsetof(Test, y);
    constexpr auto remaining = size - offset;
    unsigned char buffer[remaining];
    
    std::memcpy(buffer, &arg, remaining);
}
Run Code Online (Sandbox Code Playgroud)

在函数的末尾,缓冲区包含对象的某些成员的Test对象表示。鉴于这bar是在外部定义的,编译器无法知道在编译时是否观察到其他成员的内存foo

注意:offsetof仅对非标准布局类型有条件地支持。有问题的类是标准布局。

[基本类型]

对于任何可简单复制类型 T 的对象(除了潜在重叠的子对象),无论该对象是否持有 T 类型的有效值,构成该对象的底层字节([intro.memory])都可以复制到char、unsigned char 或 std?::?byte ([cstddef.syn]) 的数组。如果该数组的内容被复制回对象,则该对象随后将保持其原始值。

T 类型对象的对象表示是 T 类型对象占用的 N 个 unsigned char 对象的序列,其中 N 等于 sizeof(T)。...


PS 我使用了一个奇怪的例子,只观察连续的成员,因为观察前面的成员需要显式的指针运算,这在标准中有些含糊不清。我认为这样做没有实际问题,但我将其从示例中省略以将问题分开。见相关帖子