为什么编译器不能优化掉未使用的静态 std::string?

Why can compiler not optimize out unused static std::string?

如果我使用 GCC 或 Clang 编译此代码并启用 -O2 优化,我仍然会进行一些全局对象初始化。任何代码甚至有可能到达这些变量吗?

#include <string>
static const std::string s = "";

int main() { return 0; }

编译器输出:

main:
        xor     eax, eax
        ret
_GLOBAL__sub_I_main:
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:s
        mov     edi, OFFSET FLAT:_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev
        mov     QWORD PTR s[rip], OFFSET FLAT:s+16
        mov     QWORD PTR s[rip+8], 0
        mov     BYTE PTR s[rip+16], 0
        jmp     __cxa_atexit

具体来说,我没想到 _GLOBAL__sub_I_main: 部分。

Godbolt link

编辑: 即使使用简单的自定义类型,编译器仍会生成一些代码。

class Aloha
{
public:
    Aloha () : i(1) {}
    ~Aloha() = default;
private:
    int i;
};
static const Aloha a;
int main() { return 0; }

编译器输出:

main:
        xor     eax, eax
        ret
_GLOBAL__sub_I_main:
        ret

使用短字符串优化 (SSO) 编译该代码可能等同于获取 std::string 的成员变量的地址。构造函数必须在编译时分析字符串长度,并选择它是否适合 std::string 对象的内部存储,或者它必须动态分配内存但随后发现它从未被读取,因此可以优化分配代码。

在这种情况下缺乏优化可能是一个优化缺陷,仅限于像这样简单的外围示例:

const int i = 3;

int main()
{
    return (long long)(&i);  // to make sure that address was used
}

GCC 生成代码:

i:
        .long   3     ; this a variable
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, OFFSET FLAT:i
        pop     rbp
        ret

GCC 也不会优化此代码:

const int i = 3;
const int *p = &i;
int main() {  return 0; }

在文件范围内声明的静态变量,尤其是 const-qualified 可以根据 as-if 规则优化掉 除非 使用了它们的地址,GCC 只会这样做到 const-qualified 个,无论用例如何。获取变量地址是一种可观察到的行为,因为它可以传递到某个地方。将跟踪的逻辑太复杂而无法实现并且几乎没有实用价值。

当然,不使用地址的代码

const int i = 3;
int main() { return i; }

优化保留存储的结果:

main:
    mov     eax, 3
    ret

std::string 的 C++20 constexpr 构造?根据旧规则,如果结果取决于参数,则它不能是 compile-time 表达式。如果字符串太长,std::string 可能会动态分配内存,这不是 compile-time 操作。在某些情况下,目前似乎只有支持它所需的 C++20 功能的主流编译器是 MSVC。