点击上方蓝字 江湖评谈设为关注/星标
前言
C++20常量表达式(Constexpr),顾名思义:函数或者变量在编译期间,而不是运行期间就可以计算出来的结果视为常量,这种过程叫做常量表达式的计算。常量表达式在这期间起到了至关重要的作用,C++20对Constexpr进行了显著的增强,进一步扩宽了编译时求值的能力,使得编译期计算更加灵活和强大。本篇看下。
Constexpr
函数在编译期间就可以计算出其结果,constexpr
修饰,表示它可以在编译时计算。其形态如下:
constexpr 返回类型 函数名(参数列表) {
// 函数体
}
例子:
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int result = square(5); // 在编译时计算
// 此时 result 的值是 25,编译器在编译时就完成了计算
}
C++11只支持了简单的计算以及部分特性,C++20如下介绍:
1.支持容器的颗粒化操作
这些颗粒化包括,编译期间的优化,编译期间的数据长度固定,编译期间的计算以及编译期间的各种操作,例子如下:
#include <iostream>
#include <array>
constexpr int square(int x) {
return x * x;
}
constexpr int sum_of_squares(const std::array<int, 5>& arr) {
int sum = 0;
for (size_t i = 0; i < arr.size(); ++i) {
sum += square(arr[i]);
}
return sum;
}
int main() {
constexpr std::array<int, 5> arr = {1, 2, 3, 4, 5};
constexpr int result = sum_of_squares(arr); // 在编译时计算
std::cout << "Sum of squares: " << result << std::endl;
}
求数组元素的平方和结果,通过容器std::aary来存储。编译期间sum_of_squares/square函数就已经被计算出来了,且sum_of_squares传入的参数固定长度为5,sum_of_squares/square的操作分别是计算单个元素的平方和计算平方的和,且返回。
这里里面的优化是,因为sum_of_squares/square函数被标记为constexpr,所以编译期间直接计算出了结果。运行期间不需要任何运算,只需要把这个结果带入到后续的操作即可。
2.静态断言和constexpr结合操作,限制编译条件
例子,
//filename:hello.c
constexpr int max_value = 100;
static_assert(max_value <= 100, "max_value must be less than or equal to 100");
int main() {
// 如果 max_value 大于 100,这里会引发编译错误
return 0;
}
如果max_value=101的话,会报以下错误。只要小于100都会正常运行:
# g++ -std=c++20 -o hello hello.c
hello.c:3:25: error: static assertion failed: max_value must be less than or equal to 100
3 | static_assert(max_value <= 100, "max_value must be less than or equal to 100");
| ~~~~~~~~~~^~~~~~
hello.c:3:25: note: the comparison reduces to ‘(101 <= 100)’
3.constexpr支持内存分配和释放
也就是是说,在编译期间,可以动态的分配和释放内存,这也是constexpr的神奇地方。
#include <iostream>
struct MyStruct {
int x, y;
MyStruct(int a, int b) : x(a), y(b) {}
};
constexpr MyStruct* create_struct(int a, int b) {
return new MyStruct(a, b);
}
int main() {
constexpr MyStruct* ptr = create_struct(10, 20);
std::cout << ptr->x << ", " << ptr->y << std::endl;
// 不可以在 runtime 执行 constexpr 函数的动态分配
// 所以这段代码会在编译时报错。
}
4.constexpr支持其它更多操作,比如递归
#include <iostream>
#include <stdexcept>
constexpr int factorial(int n) {
if (n < 0) {
throw std::invalid_argument("Negative value is not allowed");
}
return n == 0 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // 编译时计算阶乘
static_assert(result == 120, "Compile-time factorial failed!");
std::cout << "Sum of squares: " << result << std::endl;
}
5.支持动态内存分配
比如,std:array,std::string,std::vector在编译期间使用动态内存和动态容器。
#include <vector>
#include <iostream>
constexpr int sum(const std::vector<int>& v) {
int total = 0;
for (int n : v)
total += n;
return total;
}
int main() {
constexpr std::vector<int> v = {1, 2, 3};
constexpr int result = sum(v); // 在编译时计算结果
static_assert(result == 6, "Compile-time sum failed!");
}
结尾
以上介绍了constexpr增强的地方,比如颗粒化操作,限制编译条件,内存分配释放,递归以及动态内存等等。这些东西,都可以在编译期间确定,显著的增强C++的性能。
往期精彩回顾
C++20重量级特性
C++20重量级特性:协程
C++20重量级特性:Modules
关注公众号↑↑↑:江湖评谈