2014-10-08 90 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

相關問題