2012-04-05 51 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