在昂納霧的Optimizing C++ manual他有一個部分「內聯函數有一個非內聯拷貝」,他寫道內聯函數有一個非內聯拷貝
函數內聯具有複雜性,同樣的功能可以被稱爲來自另一個模塊。編譯器必須製作內聯函數的非內聯副本,以便從另一個模塊調用該函數。如果沒有其他模塊調用該函數,則此非內聯副本是死代碼。代碼的碎片使得緩存效率降低。
讓我們來測試一下。
foo.h中
inline double foo(double x) {
return x;
}
t1.cpp
#include "foo.h"
double t1(double x) {
return foo(x);
}
的main.cpp
#include <stdio.h>
extern double foo(double);
int main(void) {
printf("%f\n", foo(3.14159));
}
編譯g++ t1.cpp main.cpp
並且它運行正常。如果我做g++ -S t1.cpp main.cpp
並查看程序集,我看到main.s
調用t1.s
中定義的函數。做g++ -c main.cpp
和g++ t1.cpp
並看着nm
的符號顯示U _Z3food
在main.o
和W _Z3food
在t1.o
。所以很明顯,Agner聲稱存在非內聯副本是正確的。
怎麼辦g++ -O1 t1.cpp main.cpp
? 由於foo
未定義,因此無法編譯。做g++ -O1 t1.cpp
和nm t1.o
顯示_Z3food
已被刪除。
現在我很困惑。我不希望g ++在啓用優化的情況下刪除非內聯副本。
看來,啓用優化inline
相當於static inline
。但沒有優化inline
意味着有一個非內聯副本生成。
也許海灣合作委員會不認爲我會永遠想要的非內聯副本。但我可以想到一個案例。假設我想創建一個庫,並且在庫中我想要一個在多個翻譯單元中定義的函數(以便編譯器可以在每個翻譯單元中內聯該函數的代碼),但是我還想要一個連接到我的庫的外部模塊能夠調用庫中定義的函數。我顯然需要一個非內聯版本的功能。
一個建議Agner給出瞭如果我不想讓非內聯副本使用static inline
。但是從這個question and answers我推斷這隻對顯示意圖有用。所以一方面很明顯,它不僅僅意圖不使用優化,因爲它使非內聯副本成爲可能。但另一方面,通過優化,它似乎只是顯示了意圖,因爲非內聯副本被剝離了。這很混亂。
我的問題:
- 是gcc正確的剝離了與優化非內嵌拷貝啓用?換句話說,如果我不使用
static inline
,應該總是有非內聯副本? - 如果我想確定沒有非內聯副本,我應該使用
static inline
?
我才意識到,我可能誤解昂納的聲明。當他說函數inlinng時,他可能會引用編譯器傾斜代碼而不是使用關鍵字inline
。換句話說,他可能指的是用extern
定義的功能,而不是inline
或static
。
例如
//foo.cpp
int foo(int x) {
return x;
}
float bar(int x) {
return 1.0*foo(x);
}
和
//main.cpp
#include <stdio.h>
extern float bar(int x);
int main(void) {
printf("%f\n", bar(3));
}
編譯gcc -O3 foo.cpp main.cpp
表明foo
是內嵌在bar
但其從未使用過的foo
非內聯拷貝是二進制。
我不知道這個標準是什麼意思,但它對於常見的情況是有意義的。通常inline/constexpr函數是在頭文件中定義的,所以它被聲明但未被定義的情況不會發生。 – kamikaze
@ kamikaze,你能更具體一些嗎?你在「它是有道理的」中指的是什麼......我不知道你的意思是什麼,但是沒有定義。我很抱歉我的問題太長了。 –
我認爲這一點是,你應該能夠創建一個指向內聯函數的指針(它將指向非內聯版本),並且具有該非內聯版本的單個實例會導致生成的代碼更少,以防多個翻譯單元需要有一個指向「內聯函數」的指針。 – PSkocik