为什么编译器为const int&生成不同的代码? vs const int参数?

时间:2014-10-08 18:04:40

标签: c++ assembly g++ language-lawyer

考虑这两个功能。

int foo(const int& a) {
    return a + 5;
}

int bar(const int a) {
    return a + 5;
}

鉴于这些功能,g ++ - 4.9在完全优化时生成以下代码。

g++-4.9 -std=c++11 -Ofast -march=native -DNDEBUG -fno-exceptions -Wall -S -o test17.S test17.cpp
# foo
movl    (%rdi), %eax
addl    $5, %eax
ret

# bar
leal    5(%rdi), %eax
ret

该机器是x86-64 Mac。

为什么编译器在这里“字面意思”?

我的理解是,C ++中的引用通常是作为指针实现的,但这并不是标准规定的。它们最好被理解为变量的别名。详细信息留待实施。

鉴于引用实际上不需要指针,为两个函数生成相同的代码似乎是一个明显的优化。为这两个函数生成const int代码是否违法?两个函数之间是否存在可观察到的差异?

1 个答案:

答案 0 :(得分:9)

您使用外部链接声明了您的功能。没有(或不能)执行全局程序优化的编译器不能改变具有外部链接的函数的接口规范,原因显而易见 - 该函数可以从其他一些翻译单元调用,该单元不知道该函数的接口规范是否为“改变了“或不。”

如果使用内部链接声明这些函数,则编译器可以更自由地转换这些函数。

当我宣布你的函数static并用gcc -O3 -fno-inline-functions -fno-inline-functions-called-once -fno-inline-small-functions编译它们时(在绝望的无限制尝试中杀死任何内联的可能性),它们产生了相同的代码

_ZL3bari:
.LFB1:
    .seh_endprologue
    leal    5(%rcx), %eax
    ret

_ZL3fooRKi.isra.0:
.LFB3:
    .seh_endprologue
    leal    5(%rcx), %eax
    ret

请注意,这些注意事项仅适用于“琐碎”函数(这可能是您首先所说的),具有“明显的”(对编译器)语义。 “非平凡”功能可能会抛弃参考的常量并修改裁判,如果裁判本身是可修改的,则完全合法。因此,在一般情况下,无法用值参数替换const引用参数。