Lambda 與普通函數的編譯器優化
Nicolai Josuttis 在他的書《C 標準庫(第二版)》中聲稱編譯器可以比普通函數更好地最佳化lambda。考慮到 lambda 表達式和普通函數都可以內聯,這似乎有悖常理。但是,兩者之間存在細微差別,可以在 lambda 的情況下進行更好的最佳化。
區別:函數對象與函數指針
Lambda 是函數對象,而普通函數本質上是函數指針。將 lambda 傳遞給函數模板時,會實例化專門針對該物件的新函數。這允許編譯器簡單地內聯 lambda 呼叫。
相反,將普通函數傳遞給函數模板會導致傳遞函數指標。編譯器歷來都在努力解決透過函數指標進行內聯呼叫的問題。雖然理論上它們可以內聯,但只有當周圍的函數也內聯時才會發生這種情況。
示例
考慮以下函數模板:
template <typename Iter, typename F> void map(Iter begin, Iter end, F f) { for (; begin != end; ++begin) *begin = f(*begin); }
使用lambda 調用它:
int a[] = { 1, 2, 3, 4 }; map(begin(a), end(a), [](int n) { return n * 2; });
將導致唯一的實例化:
template <> void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) { for (; begin != end; ++begin) *begin = f.operator()(*begin); }
編譯器可以辨識lambda 的operator() 以及對其進行簡單的內聯呼叫。
但是當用函數指標呼叫時:
map(begin(a), end(a), &multiply_by_two);
實例化變成:
template <> void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) { for (; begin != end; ++begin) *begin = f(*begin); }
這裡,每次呼叫map時f都會引用不同的函數,防止編譯器不會進行內聯調用,除非map 本身是內聯的。
結論
作為函數物件的 lambda 的獨特類型使編譯器能夠創建特定的函數實例化並無縫內聯其調用。這種增強的最佳化功能將 lambda 與普通函數區分開來,使它們成為提高程式碼效能和效率的首選。
以上是編譯器可以比普通函數更好地最佳化 Lambda 嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!