在WG21 N0131中,比尔·吉本斯指出:
缺省参数通常被认为是过时的,因为它们可以被重载函数替换
我理解,单个函数如:
void f(T t = t0, U u = u0);
可以由三个重载代替:
void f() { f(t0); }
void f(T t) { f(t, u0); }
void f(T t, U u);
但我不明白的是,为什么后者要优于前者?(这就是他所说的“时代错误”,对吗?)
Google风格指南中有一些相关的讨论:Google C++ Styleguide〉默认参数,但我看不出它如何回答这个问题或支持Gibbons的声明。
有人知道他在说什么吗?为什么违约论被认为是一种混乱?
3条答案
按热度按时间0s0u357o1#
根据我自己的经验,问题在于与其他语言特性交互时违反了最小惊讶原则。假设您有一个经常使用
f
的组件。也就是说,您在很多地方都看到了这一点:通过阅读它,你可以假设你有一个不带参数的函数,所以当你需要添加与其他一些具有注册函数的组件的交互时:
你做了显而易见的事。
...然后你马上就会得到一个漂亮的错误,因为
f
的声明类型是一个带两个参数的函数。Wtf!?所以你看了声明就明白了...对吧...默认参数通过编译器“fudging”调用位置使代码以某种方式运行。它并不是真正调用一个没有参数的函数,而是隐式初始化两个参数来调用函数。
另一方面,重载集的行为确实如人们所期望的那样。编译器没有“捏造”调用位置,当我们尝试
register(f)
时...它工作了!aiazj4mn2#
选择重载的一个客观原因
最近有人向我提出了一个客观的(我认为是这样的!:D)理由,说明为什么人们应该更喜欢重载而不是默认参数,至少在默认值是非内置类型时是这样:头文件中不必要的
#include
指令。默认参数应该是一个实现细节,因为 * 你 * 作为实现者代表你的客户决定使用什么参数,如果他们没有提供给你。为什么他们应该知道你的决定?所以当你有一个这样声明的函数
您真的希望X1 M1 N1 X是一个“秘密”,而不是暴露给您的包含者。
现在你更喜欢默认参数,你必须把它们包含在你的头文件中,所有你需要的头文件都可以写
defaultBar
。这对你来说需要多少钱?Bar
可以是基类(引用或指针),而defaultBar
是具体类的对象,因此必须同时包含定义了一个类和另一个类的头。或者,
Bar
可能是std::function<bool(Foo const&, Foo const&)>
,而它的默认值实际上是一个表达式,如[compose](https://www.boost.org/doc/libs/1_71_0/libs/hana/doc/html/compose_8hpp.html) (std::less<>{}, convertToInt)
,则在标题中会有以下内容:对于重载,标头将为
并且只有在实现中,您才会包含其他头文件
原始答案
首先,我想参考this article on FluentC++,它解决了这个问题,并在文章顶部附近给出了一个明确的个人答案:
默认情况下,我认为我们应该首选默认参数而不是重载。
然而,正如 By default 所暗示的,作者在某些特殊情况下支持重载而支持缺省参数。
我原来的回答如下,但我不得不说:上面链接的文章确实大大减少了我对违约论点的反感。
给定
void f(T t = t0, U u = u0);
,您无法使用自定义的u
调用f
,并让t
成为默认的t0
(显然,除非您手动调用f(t0, some_u)
)。有了重载,就很容易了:只需将
f(U u)
添加到重载集。因此,使用重载可以做默认参数可以做的事情,还可以做更多的事情。
此外,既然这个问题我们已经陷入了争论,为什么不提一下这样一个事实,即你可以通过添加更多的默认值来重新声明函数呢?(例子取自cppreference。)
如果函数的先前声明定义了默认参数,那么函数的定义就不能重新定义默认参数(for a pretty clear and understandable reason)。
是的,也许默认参数是一个“接口的东西”,所以可能在实现文件中看不到它的迹象是好的。
nwlls2ji3#
时代错误意味着某些东西突出地存在于现在,因为它被广泛认为是过去的事情。
我的回答的其余部分是一个观点的问题......但问题本身假设没有一个硬性的“答案”。
至于为什么默认参数已经成为过去,可能有很多例子......但是,我想到的最好的一个例子是,特别是在编写一组可重用函数时,我们希望减少错误/不正确使用的可能性。
请考虑以下事项:
现在考虑有人试图使用它如下:
他们可能希望看到以下输出:
然而他们得到的却是:
在看到输出后,他们理解了自己的错误并进行了纠正......但是如果您删除默认参数,并强制使用两个特定重载中的一个,这些重载将容纳两种类型的单个参数......那么您已经创建了一个更健壮的接口,它每次都提供预期的输出。默认参数可以工作......但它们不一定是最“清晰”的。在开发的情况下,当有人忘记如果在函数调用中提供了至少一个参数,则只有尾部参数可以被默认。
最后,重要的是代码是否有效......但是如果您看到带有标签和
goto
语句的代码,您可能会想:“哦,真的吗?"。它们工作得很好...但它们可能会被误用。切换语言来强调一般讨论的主观性质...如果JavaScript工作得很好,并提供了这么多的自由,考虑到其变量具有可变类型的性质...到底为什么会有人想要使用TypeScript?2这是一个简化/强制正确重用代码的问题。3否则谁在乎只要它能工作呢?