constexpr关键字
起因
cppreference.com上查看std::find_if函数的签名如下:
c++20前函数没有constexpr修饰,c++20及以后使用constexpr进行修饰,为什么?
c++20后添加constexpr对之前的代码会有影响吗(会不会导致之前的代码失效)?
下面来一一解答这些问题?
c++20为什么使用constexpr修饰find_if
C++20 library features中有描述该特性:
c++20 library为 和 添加了constexpr支持。
主要是为了解决(具体细节见papers P0202R3 ):
#include <array>
#include <algorithm>
int main() {
// OK
constexpr std::array<char, 6> a { 'H', 'e', 'l', 'l', 'o' };
// Failures:
// * std::find is not constexpr
constexpr auto it = std::find(a.rbegin(), a.rend(), 'H');
}
c++20后添加constexpr对之前的代码会有影响吗(会不会导致之前的代码失效)?
按照P0202R3的介绍不会有任何影响,单纯的库扩展。
这个是结论,如何实现的呢?首先我们从constexpr的作用说起:
Constexpr 起源(c++11)
Constexpr 在c++11中引入的:
详细的Paper见N2235;
概括的来讲主要是解决如下问题:
- 让编译期计算达到类型安全
- 一般来说,通过将计算移至编译期来提高效率
- 支持嵌入式系统编程(尤其是 ROM)
- 直接支持元编程
- 让编译期编程与“普通编程”非常相似
对于Constant-expression functions值得注意的一点描述有:一个constexpr function是可能被使用non-constant expressions调用的,这个时候这个函数调用就不需要编译器计算。
constexpr int func(int x)
{ return x; }
int main() {
constexpr int i = 1;
func(i); // 编译期计算
int j = 2;
func(j); // 运行期计算
return 0;
}
这也正可以解释一个函数从没有constexpr到添加constexpr关键字,是不会对原来的代码有影响的(使用no-constant expression调用时,constexpr相当于退化掉了)。
c++11,一些constexpr function例子(fine是合法,error是不合法):
// square可以解决宏函数的一些问题:类型不安全问题
constexpr int square(int x)
{ return x * x; } // fine
constexpr long long_max() { return 2147483647; } // fine
constexpr int abs(int x) { return x < 0 ? -x : x; } // fine
constexpr void f(int x) // error: return type is void
{ /* ... */ }
constexpr int next(int x)
{ return ++x; } // error: use of increment
constexpr int g(int n) // error: body not just ‘‘return expr’’
{
int r = n;
while (--n > 1) r *= n;
return r;
}
constexpr int twice(int x);
enum { bufsz = twice(256) }; // error: twice() isn’t (yet) defined
constexpr int fac(int x)
{ return x > 2 ? x * fac(x - 1) : 1; } // error: fac() not defined before use
c++14对constexpr的扩充:
C++14 允许在 constexpr 函数中使用局部变量,从而支持了循环;
#include <iostream>
constexpr int test(int n) {
int k = n; // 支持局部变量
k++;
return k;
}
int main() {
constexpr int i = 11;
int k = test(i); // 编译期计算
int j = 22;
int m = test(j); // 运行期计算
}
这种放松简化了许多 constexpr 函数并使许多 C++ 程序员感到高兴。他们不满地发现,以前在编译时只能对算法的纯函数表达式进行求值。特别是,他们希望使用循环来避免递归。就更长期来看,这释放出了要在 C++17 和 C++20中进一步放松限制的需求。为了说明潜在的编译期求值的能力,我已经指出 constexpr thread 也是可能的,尽管我并不急于对此进行提案。