2012-04-05 84 views
1

希望誰讀取該知道默認參數:參數列表中任意點的默認參數是否可能?

void setCase (string &str, int form = UPPERCASE) 
{ 
    for (char &c : str) 
     c = (form == UPPERCASE ? c & ~0x20 : c | 0x20); //this bit differentiates english uppercase and lowercase letters 
} 

int main() 
{ 
    string s1 = "HeLlO", s2 = s1, s3 = s1; 
    setCase (s1, UPPERCASE); //now "HELLO" 
    setCase (s2, LOWERCASE); //now "hello" 
    setCase (s3); //now "HELLO" due to default argument 
} 

使用默認參數的一個缺點是,你必須在列表的末尾開始拖欠參數。有時候這需要將參數重新排列成一個看起來很笨的命令。爲了解決這個問題,必須分開重載。

讓我帶一個窗口API函數,FindWindow,即通過類的姓名,職務,或兩者是找到一個窗口,作爲一個例子:

HWND WINAPI FindWindow(//returns handle to window 
    __in_opt LPCTSTR lpClassName, //param equivalent to const TCHAR *, classes are like templates for windows 
    __in_opt LPCTSTR lpWindowName //the text that appears on the title bar (for normal windows, for things like buttons, it's what text is on the button) 
); 

爲了總結這一點,可能要默認搜索選項成爲冠軍。有三種理想的實現方式(假設已經使用了其他包裝技術)。完美的解決方案很可能如下:

Window FindWindow (LPCTSTR className = 0, LPCTSTR windowName){...} 

第二種解決方案是重載函數的一個版本只接受標題,另一個同時接受。第三個是切換參數的順序。

第二個問題的主要問題是,對於較長的列表,隨着列表增長,重載的空間量可能會變得非常大。第三個問題的主要問題是,任何一個預先使用這個函數的人都會被用來首先指定類名。這也適用於常規的C++函數。參數往往具有自然順序。

第一種解決方案的主要問題當然是它不受C++語言支持。我的問題是:
未來有沒有這種可能性?

例如,編譯器能否在需要時自動生成適當的重載?

+1

不喜歡嗎?使用不同的語言,如Python,它使用[keyword arguments](http://docs.python.org/release/1.5.1p1/tut/keywordArgs.html)。 ['subprocess.Popen'](http://docs.python.org/library/subprocess.html#subprocess.Popen)就是一個很好的例子。 – 2012-04-05 03:06:33

+0

我可以和它一起生活。我只是有興趣知道(可能很多其他人也是如此),如果隨着更新的C++新增功能進入語言,這可能會發生變化。我記得另一種語言(可能是Python)適合傳遞參數,因爲能夠判斷參數是否默認或用戶是否傳遞了默認值。 – chris 2012-04-05 03:10:35

+0

也許閱讀[爲什麼可選參數必須出現在聲明的末尾](http://stackoverflow.com/questions/2896106/why-optional-parameters-must-appear-at-the-end-of-the-declaration ) 可能有幫助。 – 2012-04-05 03:11:48

回答

5

這是不可能的。有一個很多的角落案件。目前的規則非常容易學習:「所有默認參數必須出現在參數列表的末尾。」新規則是:「除非必須保留向後兼容性,否則沒有省略缺省參數的組合可能不明確。」更糟糕的是,這甚至不是你可以在定義點測試的規則,因爲C++現在甚至都沒有爲重載函數做這些。例如,採取以下兩個函數的定義:

void foo(); 
void foo(int x = 0); 

這些都是即使它是不合理的第一個一直到被調用完全合法:任何電話,看起來像foo()是模糊的。現在考慮℃的假想++版本,其中默認參數不必來結尾:

void foo(int x = 0, int y = 0); 

是什麼來foo(1)一個電話嗎?那麼,爲了向後兼容,它必須調用foo(1, 0)。這很有趣,因爲這個功能有沒有這樣的困難:

void bar(const char* a = 0, int b = 0); 

以下是該功能的一些合法的呼叫:

bar("foo"); 
bar(1); 
bar("foo", 1); 
bar(); 

所以foo功能僅產生三個版本:foo()foo(int)foo(int, int)。但是,這個也有兩個默認參數,會生成四個。 (而且它們並不是毫不含糊:foo(0)是一個模糊的調用。)好吧,你可以用標準中複雜的語言來解決這個問題。但是現在考慮這個功能:

struct A; 
struct B; 
A some_A(); 
B some_B(); 
void baz(const A& a = some_A(), const B& b = some_B()); 

現在生成版本的數量取決於你的用戶定義類型的轉換,這甚至可能不可見在你的函數的定義。在當前版本的C++中,調用baz(B())將始終嘗試將B實例轉換爲A,否則失敗。現在,有人可以合理預期在第二個參數中傳遞B實例,如果您編寫baz的四個超負載版本爲baz(),baz(const A&)baz(const B&),baz(const A&, const B&),會發生什麼情況。除非你想破壞現有的代碼,否則你甚至不能考慮在你的默認參數烏托邦世界中呼叫baz(B())不明確。

轉換也使得甚至相對簡單的情況下「由不同類型的非默認參數分隔的默認參數」有點混亂。例如,這樣的:

void quux(A* a = nullptr, B* b, C* c = nullptr); 

完美不含糊:調用作爲quux(B*)quux(A*, B*)quux(B*, C*),和quux(A*, B*, C*)。除非A繼承自C(或反之亦然)的B。當然,這與重載決議必須面對的問題是一樣的,但迄今爲止的默認論點已經完全沒有歧義,現在我們陷入了一個微妙的困境。

即使你找到一個滿足每個人的一致解決方案,簡明解釋幾乎是不可能的,這可能是一個淨虧損。

+0

您的帖子有一些好點。真正讓我感到困擾的是其他語言確實會這樣做。我確實認爲,這種補充的必要修改不必要的大,也不能真正幫助文件大小。 – chris 2012-04-05 03:26:03

+2

使用嚴格的位置參數,用戶定義的轉換和預先存在的重載解析規則執行其他語言嗎? – 2012-04-05 03:30:26

+0

C++必須以某種方式將自己分開:) – chris 2012-04-05 03:32:03