6.5 特殊用途语言特性
6.5.1 默认实参
形参在函数的多次调用中都被赋予一个相同的值,把这个反复出现的值称为函数的默认实参(default argument)。
调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
typedef string::size_type sz;
string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');
一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
使用默认实参调用函数
使用默认实参,只要在调用函数的时候省略该实参就可以了。
string window;
window = screen(); // 等价于 screen(24, 80, ' ')
window = screen(66); // 等价于 screen(66, 80, ' ')
window = screen(66, 256); // 等价于 screen(24, 256, ' ')
window = screen(66, 256, '#'); // 等价于 screen(24, 256, '#')
函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参(靠右侧位置)。
默认实参声明
在给定的作用域中一个形参只能被赋予一次默认实参。函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。
string screen(sz, sz, char = ' ');
string screen(sz, sz, char = '*'); // 错误:重复声明
string screen(sz = 24, sz = 80, char); // 正确:添加默认实参
默认实参初始值
局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。
用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时。
6.5.2 内联函数和 constexptr 函数
内联函数可避免函数调用的开销
将函数制定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。
inline const string &shorterString(const string &s1, const string &s2);
如下调用
cout << shorterString(s1, s2) << endl;
将在编译过程中展开为
cout << (s1.size() <= s2.size() ? s1 : s2) << endl;
constexpr 函数
constexpr 函数是指能用于常量表达式的函数。
定义 constexptr 函数需要遵循:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且仅有一条 return 语句。
constexpr int new_sz() { return 42; }
constexpr int foo = new_sz();
constexpr 函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。
允许 constexptr 函数的返回值并非一个常量
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
把内联函数和 constexptr 函数放在头文件内
6.5.3 调试帮助
assert 预处理宏
assert 宏使用一个表达式作为它的条件:
assert(expr);
首先对expr
求值,如果表达式为假,assert 输出信息并终止程序的执行;如果表达式为真,assert 什么也不做。
assert 宏常用与检查“不能发生”的条件。
NDEBUG 预处理变量
assert 的行为依赖于一个名为 NDEBUG 的预处理变量的状态。如果定义了 NDEBUG,则 assert 什么也不做。默认状态下没有定义 NDEBUG,此时 assert 将执行运行时检查。
预处理器定义了几个用于程序调试的名字:
_ _func_ _ 存放函数的名字
_ _FILE_ _ 存放文件名的字符串字面值
_ _LINE_ _ 存放当前行号的整型字面值
_ _TIME_ _ 存放编译时间的字符串字面值
_ _DATE_ _ 存放编译日期的字符串字面值