編譯器決策邏輯
inline 只是**建議**,編譯器根據啟發式規則(函數體大小、調用頻率、優化等級)決定是否內聯。對於 `FuncA` 調用 `FuncB`:
- FuncB 可內聯且編譯器決定內聯:`FuncB` 機器碼直接嵌入 `FuncA` 展開碼中,形成單一大塊代碼,**無函數調用指令**。
- FuncB 不可內聯 (過大、虛函數、函數指針、跨編譯單元無 LTO):`FuncA` 展開處保留 `call FuncB` 指令。**FuncA 仍可被內聯到其調用者**。
- FuncA 不內聯:生成獨立 `FuncA` 符號,內部 `call FuncB`。
關鍵點:獨立決策
`FuncA` 是否內聯、`FuncB` 是否內聯,**完全獨立決策**。無「傳遞性內聯」強制規則。
實驗驗證方法
// 編譯: g++ -O2 -S test.cpp -o test.s
// 查看 test.s 中是否有 call 指令
inline void FuncB(int x) { /* ... */ }
inline void FuncA(int x) { FuncB(x); }
- 若 `FuncB` 無 `inline` 關鍵字,GCC `-O2` 仍可能內聯 (靜態函數、單文件)。
- 跨 `.cpp` 文件需開啟 LTO (`-flto`) 才能跨單元內聯。
最佳實踐
- 頭文件中定義 `inline` 函數 (隱含 `static` 語義避免多重定義)。
- 性能關鍵路徑顯式 `inline` + `constexpr` (C++11) 強制編譯期計算。
- 勿過度內聯大函數,導致指令緩存失敗 (i-cache miss) 反而降速。