默认参数值应该放在哪个位置?只放在函数定义中,还是声明中,或者两个地方都可以?
默认参数值必须出现在函数声明中,因为这是调用者唯一看到的内容。
编辑:正如其他人指出的那样,你可以将默认参数值放在函数定义中,但我建议编写所有代码时都假设这不成立。
你可以二选一,但不能同时存在。通常情况下,你可以在函数声明时设置默认值,然后所有调用者都可以使用该默认值。然而,你也可以在函数定义时设置默认值,那么只有看到定义的人才能使用默认值。
error: default argument given for parameter <x> of <fun(args)> [-fpermissive] ..... after previous specification in <fun(args)> [-fpermissive]
。 - Patrizio BertoniC++默认参数的逻辑放在调用方,这意味着如果默认值表达式无法从调用位置计算出来,则不能使用默认值。
其他编译单元通常只包含声明,因此定义中放置的默认值表达式仅可在定义的编译单元本身中使用(且在定义后,即编译器看到默认值表达式之后)。
最有效的位置是在声明(.h)中,以便所有用户都能看到它。
有些人也喜欢在实现中添加默认值表达式(作为注释):
void foo(int x = 42,
int y = 21);
void foo(int x /* = 42 */,
int y /* = 21 */)
{
...
}
然而,这意味着出现了重复,并且会增加注释与代码不同步的可能性(有什么比没有注释的代码更糟糕的呢?有误导性的注释!)。
虽然这是一个“老”线程,但我仍然想向其添加以下内容:
我经历了下一个情况:
[需要翻译的内容包括在引用标签中,不做修改]
int SetI2cSlaveAddress( UCHAR addr, bool force );
在该类的源文件中,我有 int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
...
}
如您所见,我已将参数“force”的默认值放在类源文件中,而不是类头文件中。
然后我在一个派生类中如下使用该函数(该派生类以公共方式继承了基类):/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp: In member function 'void CMax6956Io::Init(unsigned char, unsigned char, unsigned int)':
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: error: no matching function for call to 'CMax6956Io::SetI2cSlaveAddress(unsigned char&)'
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: note: candidate is:
In file included from /home/geertvc/mystuff/domoproject/lib/i2cdevs/../../include/i2cdevs/max6956io.h:35:0,
from /home/geertvc/mystuff/domoproject/lib/i2cdevs/max6956io.cpp:1:
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: int CI2cHal::SetI2cSlaveAddress(unsigned char, bool)
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: candidate expects 2 arguments, 1 provided
make[2]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/max6956io.cpp.o] Error 1
make[1]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/all] Error 2
make: *** [all] Error 2
但是,当我在基类的头文件中添加了默认参数:
int SetI2cSlaveAddress( UCHAR addr, bool force = false );
并将其从基类的源文件中删除:
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )
那么编译器就会很高兴,并且所有的代码都可以如预期般工作(我可以向函数SetI2cSlaveAddress()
传递一个或两个参数)!
所以,不仅对于类的用户来说,在头文件中放置参数的默认值很重要,而且从编译和功能上看似乎是必须的!
我还没有看到任何人提到的一个要点是:
如果您有虚方法,每个声明都可以有自己的默认值!
使用哪个值取决于您调用的接口。
struct iface
{
virtual void test(int a = 0) { std::cout << a; }
};
struct impl : public iface
{
virtual void test(int a = 5) override { std::cout << a; }
};
int main()
{
impl d;
d.test();
iface* a = &d;
a->test();
}
它打印出50
我强烈不建议您像这样使用它。
这是因为在d.test()
和a->test()
中,尽管默认参数值分别来自于impl
和iface
,但都会执行impl
的test
函数。请参阅下面Gufino2的评论,他提到了Effective C++的第37条款。
void test(obj, a, b = a);
- Beyondo通常情况下,声明语句是最“有用”的,但这取决于您如何使用类。
两种都无效。
不错的问题... 我发现编程人员通常使用声明来声明默认值。根据编译器的不同,我曾被要求采用某种方式(或警告)。
void testFunct(int nVal1, int nVal2=500);
void testFunct(int nVal1, int nVal2)
{
using namespace std;
cout << nVal1 << << nVal2 << endl;
}
//declaration
void function(char const *msg, bool three, bool two, bool one = false);
void function(char const *msg, bool three = true, bool two, bool one); // Error
void function(char const *msg, bool three, bool two = true, bool one); // OK
//void function(char const *msg, bool three = true, bool two, bool one); // OK
int main() {
function("Using only one Default Argument", false, true);
function("Using Two Default Arguments", false);
function("Using Three Default Arguments");
return 0;
}
//definition
void function(char const *msg, bool three, bool two, bool one ) {
std::cout<<msg<<" "<<three<<" "<<two<<" "<<one<<std::endl;
}
根据标准,你可以选择其中一种方式,但请记住,如果你的代码在看到包含默认参数的定义之前看到了没有默认参数的声明,则可能会出现编译错误。
例如,如果你包含了不带默认参数列表的函数声明的头文件,则编译器将查找该原型,因为它不知道你的默认参数值,因此原型不匹配。
如果你在定义中放置带有默认参数的函数,请包含该文件,但我不建议这样做。