为什么这个未使用的变量没有被优化掉?

Max*_*ner 38 c++ gcc clang compiler-optimization

我玩过Godbolt的CompilerExplorer.我想看看某些优化是多么好.我的最低工作范例是:

#include <vector>

int foo() {
    std::vector<int> v {1, 2, 3, 4, 5};
    return v[4];
}
Run Code Online (Sandbox Code Playgroud)

生成的汇编程序(通过clang 5.0.0,-O2 -std = c ++ 14):

foo(): # @foo()
  push rax
  mov edi, 20
  call operator new(unsigned long)
  mov rdi, rax
  call operator delete(void*)
  mov eax, 5
  pop rcx
  ret
Run Code Online (Sandbox Code Playgroud)

可以看出,clang知道答案,但在返回之前会做很多事情.在我看来,即使是矢量也是由于"operator new/delete"而创建的.

任何人都可以向我解释这里发生了什么以及它为什么不回归?

GCC生成的代码(此处未复制)似乎明确构造了向量.有谁知道海湾合作委员会无法推断出结果?

Vit*_*meo 29

std::vector<T>是一个相当复杂的类,涉及动态分配.虽然clang++ 有时能够忽略堆分配,但这是一个相当棘手的优化,你不应该依赖它.例:

int foo() {
    int* p = new int{5};
    return *p;
}
Run Code Online (Sandbox Code Playgroud)
foo():                                # @foo()
        mov     eax, 5
        ret
Run Code Online (Sandbox Code Playgroud)

例如,使用std::array<T> (不动态分配) 会产生完全内联的代码:

#include <array>

int foo() {
    std::array v{1, 2, 3, 4, 5};
    return v[4];
}
Run Code Online (Sandbox Code Playgroud)
foo():                                # @foo()
        mov     eax, 5
        ret
Run Code Online (Sandbox Code Playgroud)

正如Marc Glisse在其他答案的评论中指出的那样,这就是标准在[expr.new]#10中所说的:

允许实现省略对可替换全局分配函数的调用([new.delete.single],[new.delete.array]).当它这样做时,存储由实现提供,或者通过扩展另一个新表达式的分配来提供.实现可以扩展新表达式e1的分配,以便为新表达式e2提供存储,如果以下情况属实,则分配未扩展:[...]

  • 根据[澄清记忆分配](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html),这些是否可观察或不.从那时起确切地看到发生了什么变化将会很有趣...... (3认同)

MSa*_*ers 7

如评论所述,operator new可以替换.这可能发生在任何翻译单位.因此,为未被替换的案例优化程序需要全程序分析.如果它替换,你当然必须调用它.

是否未指定默认值operator new是库I/O调用.这很重要,因为库I/O调用是可观察的,因此它们也无法优化.

  • @RichardHodges:向量不允许SSO,因为交换两个小向量然后需要交换它们的元素,并且不允许交换容器移动,复制或交换任何元素(`array`是一个例外,`string`是不是真正的容器;参见[container.requirements.general]/9). (4认同)
  • "允许实现省略对可替换全局分配函数的调用"< - 直接来自标准[expr.new] (4认同)