2014-10-08 92 views
1

考虑这两个功能。为什么编译器为const int&vs const int参数生成不同的代码?

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的苹果。

为什么编译器会这么“注重文字”?

我的理解是C++中的引用通常是作为指针来实现的,但这并不是标准所要求的。他们最好被理解为变量的别名。细节留给实施。

鉴于引用实际上不需要指针,所以对于这两个函数都生成相同的代码似乎是一个明显的优化。为这两个函数生成const int代码是不合法的?两个函数之间有明显的区别吗?

+6

ABI要求将引用作为指针传递。它这样做是因为函数可能会查看其参数的地址,而不仅仅是它的值。 – 2014-10-08 18:10:31

+1

调用者站点可能需要知道差异,所以优化需要在调用站点和被调用方法之间达成一致。看到链接器可能会涉及,这是一个难题。 – 2014-10-08 18:10:43

+2

在多线程应用程序的上下文中可以有可观察的差异:在引用foo和返回表达式的评估之间引用的值可能会发生变化(a + 5) – 2014-10-08 18:20:55

回答

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引用参数。

+0

从你指出,我也能够通过声明foo()和bar()静态并从包装函数调用它们来获得相同的代码。 – Praxeolitic 2014-10-08 18:37:14

+0

@AndreyT'-O4'? – edmz 2014-10-08 18:45:17

+0

@black:好吧,让我们把它做成'-O3'(我用'-O4'实际运行它。)越多越好...... :) – AnT 2014-10-08 18:46:05

相关问题