测试环境:windows10 + gcc8.1
1、constexpr产生背景
c++11以后,为了保证写出的代码比以往任何时候的执行效率都要好而进行了许多改善。其中,这种改善之一就是生成常量表达式,允许程序利用编译时的计算能力。常量表达式主要是允许一些计算发生在编译时期,而不是运行时期。这是一个很进步的优化:假如有些事情可以在编译时计算,它将只计算一次,而不是在运行时每一次都进行计算。需要计算一个编译时已知的常量,比如特定的sin或者cos值,确实可以使用库函数sin和cos,但那样做是必须花费运行时的开销。此时可以使用constexpr创建一个编译时的函数,它将在编译时期计算出你需要的数值,而在用户的电脑上将无需做这些工作。
2、constexpr用法
为了使函数获取编译时计算的能力,必须给该函数指定constexpr关键字。
constexpr int multiply(int x,int y) { return x* y; } //将在编译时期计算 const int var = multiply(10,10);
除了编译时计算性能的优化,congtexpr的另外一个优势是:允许函数被应用到以前调用宏的所有场合。例如:想要计算数组size的函数,size是10的倍数。如果不用constexpr,则需要创建一个宏或者模板,因为我们不能用函数的返回值去声明数组的大小。但是我们可以调用一个constexpr函数去声明一个数组。
constexpr int getDefaultArraySize(int value) { return value*10; } int my_array[getDefaultArraySize(3)];
3、constexpr使用限制
c++11中的constexpr指定的函数返回值和参数都必须保证是字面值,而且必须有且只有一行代码(return代码)。所以通常只能通过return 三目运算符+递归来计算返回的字面值。
constexpr int factorial (int n) { return n > 0 ? n * factorial( n - 1 ) : 1; }
c++14中则只要保证返回值和参数是字面值就行,函数体中可以加入更多的语句,实现了更灵活的计算。
// C++14 constexpr int factorial2(int n) { int result = 1; for (int i = 1; i <= n; ++i) result *= i; return result; }
c++17中lambda表达式可以被声明为constexpr。对于一个lambda而言,只要被捕获的变量是字面量类型(lieteral type),那么整个lambda也将表现为字面量类型。
//显示声明为constexpr类型 template <typename T> constexpr auto addTo(T i){ return[i](auto j){return i+j;}; } constexpr auto add5 = addTo(5);
当一个闭包再constexpr环境下被使用时,当它满足了constexpr的条件,则无论它有没有被显示地声明为constexpr,它仍然是constexpr的。
//这里没有显式声明为constexpr,但依然可以表现为constexpr auto answer = [](int n) { return 32 + n; }; //在一个constexpr环境中被使用 constexpr int response = answer(10);
在c++17 中contexpr if 让以前理应被写在一起的代码,却在c++17前都没法被写在一起的情况得到了改善。传统的if-else语句是在执行期进行条件判断与选择的,因而在泛型编程中,无法使用if-else语句进行判断。在c++17中,我们可以在编译期对传统的条件语句做出相应判断,可以忽略那些完全没有被进入的语句。
注意,在老的标准中,计算使用了if,另一个分支也仍然会被编译,但在c++17中,如果使用if constexpr来替代if,编译器甚至会把编译无效条件这个过程忽略掉。
constexpr对STL库标准做出的改进:
以前在标准库中,有许多类型和函数都缺乏了constexpr的特性,这些问题在C++17中都相应做了改进。最著名的就是std::array以及用于范围获取的std::begin()和std::end()。