默认参数值在哪里?只是在函数定义,或声明,或两个地方?
10 回答
默认参数值必须出现在声明中,因为这是调用者看到的唯一内容。
编辑:正如其他人指出的那样,您可以对定义提出论点,但我建议编写所有代码,就好像那不是真的一样。
你可以做任何一个,但不能两者兼而有之。通常您在函数声明时执行此操作,然后所有调用者都可以使用该默认值。但是,您可以在函数定义处执行此操作,然后只有那些看到定义的人才能使用默认值。
C++将默认参数逻辑放在调用方,这意味着如果无法从调用方计算出默认值表达式,则无法使用默认值。
其他编译单元通常只包含声明,因此放置在定义中的默认值表达式只能在定义编译单元本身中使用(并且在定义之后,即在编译器看到默认值表达式之后)。
最有用的地方是在声明 (.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”的默认值放在了类源文件中,而不是放在类头文件中。
然后我在派生类中使用该函数如下(派生类以公共方式继承基类):
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 addr, bool force )
然后编译器很高兴,所有代码都按预期工作(我可以给函数一两个参数SetI2cSlaveAddress()
)!
因此,不仅对于类的用户而言,将参数的默认值放在头文件中很重要,而且在编译和功能方面,这显然是必须的!
如果函数是公开的——非成员、公共或受保护的——那么调用者应该知道它们,并且默认值必须在标头中。
如果函数是私有的并且是离线的,那么将默认值放在实现文件中确实是有意义的,因为这允许不触发客户端重新编译的更改(对于企业级共享的低级库有时是一个严重的问题发展)。也就是说,这肯定会令人困惑,并且在标题中以更直观的方式呈现 API 具有文档价值,因此请选择您的折衷方案——尽管在没有任何令人信服的理由时,一致性是主要的事情。
还有一点我没有找到任何人提到:
如果你有虚方法,每个声明都可以有自己的默认值!
这取决于您调用的接口将使用哪个值。
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
我强烈不鼓励你像这样使用它
该声明通常是最“有用的”,但这取决于您希望如何使用该类。
两者都无效。
好问题...我发现编码人员通常使用声明来声明默认值。基于编译器,我一直坚持一种方式(或警告)或另一种方式
void testFunct(int nVal1, int nVal2=500);
void testFunct(int nVal1, int nVal2)
{
using namespace std;
cout << nVal1 << << nVal2 << endl;
}
您可以在任何一个中执行(根据标准),但请记住,如果您的代码在包含默认参数的定义之前看到没有默认参数的声明,则可能会出现编译错误。
例如,如果您包含包含函数声明但没有默认参数列表的标头,则编译器将查找该原型,因为它不知道您的默认参数值,因此原型将不匹配。
如果您在定义中放置带有默认参数的函数,则包含该文件,但我不建议这样做。
再加一分。带有默认参数的函数声明应该从右到左和从上到下排序。
例如,在下面的函数声明中,如果您更改声明顺序,那么编译器会给您一个缺少默认参数的错误。原因编译器允许您在同一范围内将函数声明与默认参数分开,但它应该按照从 RIGHT 到 LEFT(默认参数)和从 TOP 到 BOTTOM(函数声明默认参数的顺序)的顺序。
//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;
}