2011-07-27 50 views
8

我已經花了幾天時間在一個奇怪的問題,並最終發現有兩個inline功能在項目中相同的簽名,他們造成了這個問題。爲了簡化這裏的情況就是一個例子:兩個CPP文件:如果重新定義內聯函數會怎樣?

a.cpp

#include <iostream> 

void b(); 

inline void echo() 
{ 
    std::cout << 0 << std::endl; 
} 

int main() 
{ 
    echo(); 
    b(); 
    return 0; 
} 

和b.cpp

#include <iostream> 

inline void echo() 
{ 
    std::cout << 1 << std::endl; 
} 

void b() 
{ 
    echo(); 
} 

請注意:inline功能echo具有相同簽名但不同實現。編譯並運行

g++ a.cpp b.cpp -o a.out && ./a.out 

或者這樣

g++ a.cpp -c 
g++ b.cpp -c 
g++ a.o b.o -o a.out 
./a.out 

它打印0 0。 (我用的是G ++ 4.6.1是什麼,以及我鏗鏘++ 2.9,相同的結果測試)

如果開啓優化,像

g++ -O3 a.cpp b.cpp -o a.out && ./a.out 

這是0 1這個時候,就不會發生。

我的問題是,無論結果如何或編譯如何執行,都沒有錯誤甚至是警告我已多次定義了inline函數。編譯器和鏈接器在這種情況下究竟發生了什麼?

編輯:

採取目標文件

nm a.o b.o | c++filt 

兩個文件都記錄在echo()看看符號。所以我認爲問題發生在鏈接時。難道說鏈接器會隨機選擇一個實現並丟棄所有其他實現嗎?

+0

你有沒有試過更高級的警告(-Wall等)? – schnaader

+0

我剛剛嘗試過'-Wall -Wextra',仍然沒有任何警告。 – neuront

回答

5

編譯器不需要診斷此ODR違例,它不是微不足道的。 inline關鍵字表示不同的翻譯單元可能具有相同的符號,因此編譯器將其標記爲弱。基本用例是一個在頭文件中內聯定義的函數:包含頭文件的所有翻譯單元都將具有該定義,並且它非常好。編譯器只需要放棄除一個定義之外的所有定義,並在任何地方使用該定義。

檢測不同的定義是否完全匹配是一個複雜的問題。鏈接器必須分析生成的二進制實現,並確定這兩個二進制代碼是否與相同的源代碼相關。大多數編譯器沒有支持來確定這一點。

當你的具體問題,我不可能知道,導致了兩個功能被嵌入標記的理由,但一個常見的錯誤是使用inline關鍵字來表示優化而非在不抱怨的重複鏈接時間inline關鍵字在頭文件中有意義,但在cpp文件中沒有這麼多。在cpp文件中,如果要將某段代碼分解到輔助函數中,該函數應標記爲static或在未命名的命名空間中定義。如果函數是static,那麼編譯器知道該函數的所有用法都在你的翻譯單元中,並且它有更多的知識來決定是否內聯或不內函數(注意,即使你不用不要告訴它,就像它可以決定不內聯即使你告訴它一樣)。

12

在C++標準中指出比內聯函數的所有定義應是相同的,但沒有診斷是必需的。也就是說,你的程序不是一個有效的C++程序,但是實現有權不檢測那個錯誤。

參見條款3.2.5。張貼這裏太長了。

6

這種情況下(兩個內聯函數具有相同的名稱和相同的簽名具有不同的實現)leads to undefined behavior。編譯器不需要診斷它,儘管它可以嘗試。

相關問題