默认参数值的位置是什么?只是在函数定义或声明中,还是两个地方都有?
当前回答
虽然这是一个“老”线程,我仍然想添加以下内容:
我经历了下一个案例:
在类的头文件中,有
int SetI2cSlaveAddress(UCHAR addr, bool force);
在那个类的源文件中,我有
int CI2cHal::SetI2cSlaveAddress(UCHAR地址,bool force = false) { ... }
可以看到,我将参数“force”的默认值放在类源文件中,而不是类头文件中。
然后我在派生类中使用该函数,如下所示(派生类以公共方式继承基类):
SetI2cSlaveAddress(addr);
假设它将“force”参数视为“理所当然”的“false”。
然而,编译器(放在c++11模式)抱怨,并给了我以下编译器错误:
/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地址,bool force)
然后编译器很高兴,所有代码都按预期工作(我可以给函数SetI2cSlaveAddress()一个或两个参数)!
因此,不仅对于类的用户来说,将参数的默认值放在头文件中很重要,而且编译和功能方面显然是必须的!
其他回答
c++将默认参数逻辑放在调用端,这意味着如果不能从调用端计算默认值表达式,则不能使用默认值。
其他编译单元通常只包含声明,因此放置在定义中的默认值表达式只能在定义编译单元本身中使用(并且在定义之后,即在编译器看到默认值表达式之后)。
最有用的地方是在声明(.h)中,这样所有用户都能看到它。
有些人也喜欢在实现中添加默认值表达式(作为注释):
void foo(int x = 42,
int y = 21);
void foo(int x /* = 42 */,
int y /* = 21 */)
{
...
}
然而,这意味着重复,并将增加注释与代码不同步的可能性(没有注释的代码会更糟糕吗?带有误导性注释的代码!)。
声明通常是最有用的,但这取决于你想如何使用这个类。
两者都是无效的。
你可以使用任何一种(根据标准),但请记住,如果你的代码在包含default参数的定义之前看到了没有default参数的声明,那么编译错误就会出现。
例如,如果你包含头包含函数声明没有默认参数列表,因此编译器将寻找原型,因为它不知道你的默认参数值,因此原型将不匹配。
如果你把函数和默认参数放在定义中,那么包括这个文件,但我不建议这样做。
还有一点我没有发现任何人提到过:
如果你有虚方法,每个声明都可以有自己的默认值!
这取决于您调用的接口将使用哪个值。
ideone上的例子
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个
我强烈建议您这样使用它
虽然这是一个“老”线程,我仍然想添加以下内容:
我经历了下一个案例:
在类的头文件中,有
int SetI2cSlaveAddress(UCHAR addr, bool force);
在那个类的源文件中,我有
int CI2cHal::SetI2cSlaveAddress(UCHAR地址,bool force = false) { ... }
可以看到,我将参数“force”的默认值放在类源文件中,而不是类头文件中。
然后我在派生类中使用该函数,如下所示(派生类以公共方式继承基类):
SetI2cSlaveAddress(addr);
假设它将“force”参数视为“理所当然”的“false”。
然而,编译器(放在c++11模式)抱怨,并给了我以下编译器错误:
/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地址,bool force)
然后编译器很高兴,所有代码都按预期工作(我可以给函数SetI2cSlaveAddress()一个或两个参数)!
因此,不仅对于类的用户来说,将参数的默认值放在头文件中很重要,而且编译和功能方面显然是必须的!
推荐文章
- 为什么这个结合赋值和相等检查的if语句返回true?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c++枚举类可以有方法吗?
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?
- main()中的Return语句vs exit()
- 为什么c#不提供c++风格的'friend'关键字?
- 在函数的签名中添加关键字
- 我如何在Visual Studio中预处理后看到C/ c++源文件?
- 为什么在标准容器中使用std::auto_ptr<>是错误的?
- 用比较double和0
- 保护可执行文件不受逆向工程的影响?
- 在c++中字符串前面的“L”是什么意思?
- 为什么std::map被实现为红黑树?