内联函数和constexpr函数
内联函数
下面的例子中:
// find longer of two strings
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size() ? s1 : s2;
}
为这样的小操作定义一个函数的好处是:
阅读和理解函数 shorterString 的调用,要比读一条用等价的条件表达式取代函数调用表达式并解释它的含义要容易得多。
如果需要做任何修改, 修改函数要比找出并修改每一处等价表达式容易得多。
使用函数可以确保统一的行为,每个测试都保证以相同的方式实现。
函数可以重用,不必为其他应用重写代码。
但是,将 shorterString 写成函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作;调用前要先保存寄存器,并在返回时恢复;复制实参;程序还必须转向一个新位置执行。
inline 函数避免函数调用的开销
将函数指定为 inline 函数, (通常)就是将它在程序中每个调用点上“内联地”展开。假设我们将 shorterString 定义为内联函数,则调用:
cout << shorterString(s1, s2) << endl;
在编译时将展开为:
cout << (s1.size() < s2.size() ? s1 : s2)<< endl;
从而消除了把 shorterString 写成函数的额外执行开销。
// inline version: find longer of two strings
inline const string & shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size() ? s1 : s2;
}
inline 说明对于编译器来说只是一个建议,编译器可以选择忽略这个。
一般来说,内联机制适用于优化小的、只有几行的而且经常被调用的函数。大多数的编译器都不支持递归函数的内联。一个 1200 行的函数也不太可能在调用点内联展开。
constexpr函数
constexpr函数是指用于常量表达式的函数。定义constexpr函数时需要注意:函数的返回类型以及所有形参的类型都得是字面值类型,而且函数体中必须有且仅有一条return语句。
constexpr int new_sz(){return 42;}
constexpr int foo=new_sz();
把new_sz定义成无参数的constexpr函数,因为编译器能在程序编译时验证new_sz函数返回的是常量表达式,所以可用new_sz函数初始化constexpr类型的变量foo。
执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值,为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。
Constexpr函数体内也可以有其他语句,但是这些语句必须保证在运行时不执行任何操作。例如:空语句,类型别名,以及using声明。
允许constexpr函数返回值并非常量:
constexpr size_t scale(size_t cnt){return new_sz()*cnt;}
当scale的实参是常量表达式时,它的返回值也是常量表达式,反之则不然:
int arr[scale(2)]; // 正确,scale(2)是常量表达式
int i=2;
int a2[scale(i)]; //错误,scale(i)不是常量表达式
把 inline 函数和constexpr放入头文件
内联函数和constexpr函数应该在头文件中定义,这一点不同于其他函数。毕竟编译器能够在调用点展开该函数的代码。此时,仅有函数原型是不够的。
不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须一致,基于这个原因,内联函数和constexpr函数通常定义在头文件中。