Eol
Thanks for reading :)
Eol's Blog

C++ 默认参数注意事项总结

C++ 默认参数注意事项总结

重复声明

不含默认参数的函数可以重复多次声明,而含有默认参数的函数在重复声明时:

  • 默认参数的默认值不能重复声明。
  • 默认参数的默认值同样也不能修改。

追加默认参数

非默认参数在后续声明时可追加默认值,但需保证追加的 n 个新默认参数同样是原非默认参数中的最后 n 个。考虑到 #重复声明 中提到的要求,已存在默认值的参数不能再次声明默认值。

例如,有以下函数声明:

void f(int a, int b, int c, int d = 0);

若要为其中的参数 bc 追加默认值时,可以以如下形式:

void f(int a, int b = 2, int c = 1, int d);

追加默认参数的特性适合用于不同文件中相同参数出现不同高频初始值的情况。

默认值无需编译时确定

int rand_gen()
{
    return rand() % 10;
}

void foo(int a = rand_gen());

以上默认参数的声明是完全正确的。调用 foo() 时,程序会先执行 rand_gen(),并以返回值初始化参数 a

默认值不得依赖形参列表中其他参数

声明默认参数时,默认参数的值虽然无需编译时确定,但仍需要保证不依赖其他默认参数。

例如,以下代码

int g(int);
void foo(int a = b, int b = 0);
void bar(int a = 0, int b = g(a));

上述示例中,gcc(7.4.0) 与 clang(6.0.0) 对 foo 函数与 bar 函数的声明均报 error。其中对 foo 声明的报错信息均是指出 a 声明时 b 尚未声明,而对 bar 函数声明的报错则不相同(可自行实验)。

事实上,C++ 标准(截至 C++ 17)并未规定函数参数的初始化顺序,因此,foobar 中的两个参数何者先初始化依赖编译器实现,自然不能够允许其间出现依赖关系。明明只要规定一下就完全没有问题的 _(•̀ω•́ 」∠)_

Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in default argument expressions, even if they are not evaluated.

§8.3.6/9, ISO/IEC 14882:2003

特别地,当含有默认参数的函数为成员函数时,极易犯类似问题:

(我不会说这是因为我错过两次)

struct X
{
    X():mem(42){}
    void f(int param = mem) //ERROR
    {
        //do something
    }
private: 
    int mem;
};

成员函数实际的参数比声明中多一个 this 指针。因此,看似只有一个参数的 X::f 事实上的原型为

void f(X* this, int param = this->mem);

因此 param 参数和第一个参数 this 便出现了依赖关系。

参考链接:https://stackoverflow.com/questions/4539406/nonstatic-member-as-a-default-argument-of-a-nonstatic-member-function

Eol

Author

Leave a Reply

textsms
account_circle
email

Eol's Blog

C++ 默认参数注意事项总结
重复声明 不含默认参数的函数可以重复多次声明,而含有默认参数的函数在重复声明时: 默认参数的默认值不能重复声明。 默认参数的默认值同样也不能修改。 追加默认参数 非…
Scan QR code to continue reading
2019-09-02