6.5 特殊用途语言特性
介绍三种函数相关的语言特性,分别是:默认实参、内联函数和constexpr函数,以及在程序调试过程中常用的一些功能。
6.5.1 默认实参
某些函数有这样一种形参,在函数的很多次调用中它们被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
默认实参作为形参的初始值出现在形参列表中,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
使用默认实参调用函数
如果我们想使用默认实参,只要在调用函数的时候省略该实参就可以了。函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参(靠右侧位置)。
当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面。
默认实参声明
在给定的作用域中一个形参只能被赋予一次默认实参,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。
通常,应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。
默认实参初始值
局部变量不能作为默认实参。
用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时:
6.5.2 内联函数和constexpr函数
把规模较小的操作定义成函数有很多好处,但是调用函数一般比求等价表达式的值慢一些。
内联函数可以避免函数调用的开销
将函数指定为内联函数,通常就是将它在每个调用点上“内联地”展开。内联机制用于优化规模较小、流程直接、频繁调用的函数。
在函数的返回类型前面加上关键字inline,这样就可以将它声明成内联函数了。
constexpr函数
constexpr函数是指能用于常量表达式的函数。函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。constexpr函数被隐式地指定为内联函数。
constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。
允许constexpr函数的返回值并非一个常量。
6.5.3 调试帮助
assert预处理宏
assert是一种预处理宏,所谓预处理宏其实是一种预处理变量。assert宏使用一个表达式作为它的条件:assert(expr);
首先对expr求值,如果表达式为假,assert输出信息并终止程序的执行。如果表达式为真,assert什么也不做。
assert宏定义在cassert头文件中。
NDEBUG预处理变量
assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做。默认状态下没有定义NDEBUG,此时assert将执行运行时检查。
可以使用一个#define语句定义NDEBUG,从而关闭调试状态。
6.6 函数匹配
当几个重载函数的形参数量相等以及某些形参的类型可以由其他类型转换得来时,便不容易确定应该选择哪个重载函数。
确定候选函数和可行函数
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。可行函数有两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者能转换为形参的类型。
函数匹配的第三步是从可行函数中选择与本次调用最匹配的函数。逐一检查函数调用提供的实参,寻找形参类型与实参类型最匹配的那个可行函数。
含有多个形参的函数匹配
当实参的数量有两个或者更多时,也是先选择可行函数。接下来,编译器依次检查每个实参以确定哪个函数是最佳匹配。如果有且只有一个函数满足下列条件,则匹配成功:
该函数每个实参的匹配都不劣于其它可行函数需要的匹配;只有有一个实参的匹配优于其它可行函数提供的匹配。
如果在检查了所有实参之后没有发现任何一个函数脱颖而出,编译器将报告二义性调用信息。
6.6.1 实参类型转换
为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下:
6.7 函数指针
函数指针指向的是函数而非对象。函数指针指向某种特定的类型,函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
*pf 两端的括号必不可少。如果不写这对括号,则pf是一个返回值为bool指针的函数。
使用函数指针
当我们把函数名作为一个值使用时,该函数自动地转换成指针。
在指向不同函数类型的指针间不存在类型转换。可以为函数指针赋一个nullptr或者值为0的常量表达式,表示该指针没有指向任何一个函数。
重载函数的指针
如果定义了指向重载函数的指针,编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某一个精确匹配。
函数指针形参
虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。此时,形参看起来是函数类型,实际上却是当成指针使用:
直接使用函数指针类型显得冗长而烦琐。类型别名和decltype能让我们简化使用了函数指针的代码。
decltype返回的是函数类型,此时不会讲函数类型自动转换成指针类型,所以只有在结果前面加上*才能得到指针。
返回指向函数的指针
虽然不能返回一个函数,但是可以返回指向函数类型的指针。我们必须把返回类型写成指针形式,编译器不会自动地将函数返回类型当成对应的指针类型处理。
f1的直接声明方式:
使用尾置返回类型的凡是声明:
将auto和decltype用于函数指针类型
如果我们明确知道返回的函数是哪一个,就能使用decltype简化书写函数指针返回类型的过程。