Guru of the Week 條款27:轉呼叫函式 (轉)
GotW#27 轉呼叫(Forwarding Functions):namespace prefix = o ns = "urn:schemas--com::office" />
難度:3 / 10
怎樣將轉呼叫函式寫得最好?原本答案很簡單,但我們已經知道C++語言近來發生了微妙的變化。
問題
轉呼叫函式對於將任務傳遞給其它函式或時很有用,尤其當它們被設計得很高效時。
評論一下下面這個轉呼叫函式。你試圖修改它嗎?如果是的話,怎麼來?
// file f.cpp
#include "f.h"
/*...*/
bool f( X x ) {
return g( x );
}
(說明:本次GotW的目的之一是闡明在July [1997]於London加入到C++語言中的一個微妙改進所造成的後果。)
解答
轉呼叫函式對於將任務傳遞給其它函式或物件時很有用,尤其當它們被設計得很高效時。
關鍵點是:。
評論一下下面這個轉呼叫函式。你試圖修改它嗎?如果是的話,怎麼來?
// file f.cpp
#include "f.h"
/*...*/
bool f( X x ) {
return g( x );
}
有兩個主要改進可使得這個函式更高效。第一個應該總被採用,第二個需要權衡。
1.傳參時使用傳const的引用代替傳值
“這不會造成混亂嗎?”你可能會問。不,它不會,至少在這種情況下。直到最近,C++語言才規定:因為可以確保引數x除了被傳遞給g()外沒有被其它地方使用,編譯器可以將x完全掉。例如,這樣的程式碼:
X my_x;
f( my_x );
編譯器可以:
a)產生一個my_x的複製供f()使用(就是f()的程式碼體中的形參x),然後將這個複製傳給g();或者
b)直接將my_x傳給g()而不生成複製,因為它注意到這個額外的複製除了作g()的引數外根本沒被使用。
後者更高效,不是嗎?這是編譯器試圖作的最佳化,不是嗎?
是的,是的,但只到July 1997的London會議。在那次會議上,“限制編譯器作這種取消額外複製的最佳化”的提案得到了更多的支援。〖注1〗編譯器唯一可以取消額外複製構造的地方是“返回值最佳化”(在你的C++寶典中查詢細節吧)和“臨時物件”。
這意味著,象f這樣的轉呼叫函式,編譯器被要求產生兩份複製。既然我們(作為f的作者)知道這個額外的複製不是必須的,我們應該按照通常的辦法將x申明為const X&型的引數。
(注意:如果我們一直就是這麼做的,而不是依賴於知道編譯器被允許做些什麼,那麼,這個規則的變化不會對我們造成任何影響。這就是一個“簡單就是美”例子--儘可能避開語言的細枝末節,別耍小聰明。)
2.函式內聯
這個需要權衡。要之,預設將所有函式都實現為外聯,有選擇地將確實需要內聯以提高效率的函式實現為內聯。
當你將函式內聯時,積極面是你避免了對f函式的的額外開銷。
消極面是內聯f暴露了f的實現,並使得的程式碼依賴於此實現,當f被改變時,所有的使用者程式碼都必須被重編譯。更嚴重的是,使用者程式碼現在至少需要知道函式g()的原型,這有點噁心,因為使用者根本沒有直接呼叫函式g,原本可以根本不需要知道它的原型的(至少,從我們的例子上,是這樣的)。於是,如果g()自己發生了變化,接受其它型別的其它引數時,使用者的程式碼將變得也需要知道這些型別的申明。
內聯和非內聯都可以。必須在優缺點間進行權衡,取決於f現在是怎樣被使用的以及使用的廣泛程度,和將來可能變為怎樣被使用的以及使用的廣泛程度。
GotW給出的程式碼規範:
l 傳參時,用傳const的引用來代替傳值
l 避免函式內聯,除非profiler告訴你有這個必要(員在猜測效能瓶頸點方面是很不準的)
注1:這個改進是必要的,它避免了編譯器未經允許地省略複製構造時帶來問題,尤其當複製構造有副作用時。很多時候,程式碼需要計算物件的複製數目。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992727/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 條款05: 瞭解c++默默編寫並呼叫哪些函式C++函式
- oracle函式手冊(轉)Oracle函式
- Hive行轉列函式Hive函式
- 全形轉半形函式函式
- MySQL內建函式:year()、 week()相容MySql函式
- 條款24:若所有引數皆需型別轉換,請為此採用non-member函式型別函式
- ORACLE分析函式手冊(轉)Oracle函式
- 8.轉換文字函式函式
- Oracle OCP(05):轉換函式Oracle函式
- 【函式】Oracle12c 列轉行函式使用listagg函式Oracle
- 子函式呼叫函式
- 函式呼叫棧函式
- ORACLE分析函式手冊二(轉)Oracle函式
- oracle Forms Builder常用函式 (轉載)OracleORMUI函式
- 轉MySQL--mysql常用函式打全MySql函式
- 條件函式函式
- GaussDB: db2->gaussdb 函式轉換DB2函式
- GO 的鏈式呼叫寫一個轉碼庫Go
- 外部函式的呼叫函式
- gdb 如何呼叫函式?函式
- C程式函式呼叫&系統呼叫C程式函式
- python函式每日一講 - float函式型別轉換詳解Python函式型別
- MySQL函式-條件判斷函式MySql函式
- PostgreSQL函式裡呼叫函式(SETOF + RETURN QUERY)SQL函式
- 普通函式與函式模板呼叫規則函式
- Oracle中REGEXP_SUBSTR函式(字串轉多行)Oracle函式字串
- sql server 資料型別轉換函式SQLServer資料型別函式
- 函式匹配和實參型別轉換函式型別
- C++型別轉換建構函式C++型別函式
- 將數值轉換為字串的函式字串函式
- 分割槽函式partition by的基本用法【轉載】函式
- 自定義跳轉函式的通用unhook方法函式Hook
- impala 條件函式函式
- vscode 跳轉到函式之後怎麼跳轉回之前的位置VSCode函式
- 普通函式與函式模板呼叫規則2函式
- httprunner yml 呼叫外部函式HTTP函式
- 建構函式定義的隱式型別轉換函式型別
- [轉]Cocos2d-x下Lua呼叫自定義C++類和函式的最佳實踐C++函式
- SqlServer中將字串轉utf-8的函式、支援中文的UrlEncode函式SQLServer字串函式