constexpr 函数
在 C++ 中,constexpr 是一个关键字,用于声明一个函数,该函数可以在编译时(compile time)进行求值。这意味着,如果 constexpr 函数的参数在编译时是已知的值,那么编译器就可以直接计算出函数的结果,并将结果嵌入到最终的可执行代码中,而不是在程序运行时再进行计算。
你可以把 constexpr 函数想象成一个超级机智的厨师,如果提前知道你需要多少食材,他就能在你真正开始做饭之前就把它们准备好,这样你就不用在做饭的时候再花时间去切菜或者量米了。
constexpr 函数的主要目的是:
- 提高性能:通过在编译时进行计算,可以减少程序运行时的计算量,从而提高程序的执行速度。
-
用于常量表达式:
constexpr函数的结果可以用在需要常量表达式的上下文中,例如定义数组的大小、作为模板参数、或者初始化constexpr变量。 -
进行编译时检查:如果
constexpr函数的参数在编译时不是常量表达式,编译器可能会发出警告或错误。
constexpr 函数的语法:
声明一个 constexpr 函数与声明普通函数类似,只需要在函数返回类型前面加上 constexpr 关键字。
constexpr 返回类型 函数名(参数列表) {
// 函数体
}
constexpr 函数的要求和限制:
为了能够在编译时进行求值,constexpr 函数需要满足一些限制:
-
函数体限制:
-
在 C++11 和 C++14 中,
constexpr函数的函数体必须超级简单,一般只能包含单一的return语句(除了typedef、using声明和静态断言static_assert)。 -
从 C++14 开始,
constexpr函数的限制被放宽,可以包含更复杂的语句,例如循环、条件判断、局部变量等,但这些语句不能有副作用(例如修改全局变量或调用非constexpr函数)。
-
在 C++11 和 C++14 中,
-
只能调用其他
constexpr函数或特定的内置函数:constexpr函数内部调用的任何函数也必须是constexpr函数,或者是一些特定的可以在常量表达式中使用的内置函数。 -
不能有副作用:
constexpr函数不能修改全局变量或具有任何其他的副作用。它们的主要目的是计算并返回值。 -
参数和返回类型必须是字面值类型(Literal Types):字面值类型包括算术类型(
int、float等)、void、指针类型、引用类型以及某些用户自定义类型(如果它们的构造函数和析构函数满足特定条件)。
constexpr 函数的求值时机:
一个 constexpr 函数可以在编译时求值,也可以在运行时求值。
-
编译时求值:如果调用
constexpr函数时,传递给它的参数是常量表达式(例如字面量、const变量、其他constexpr函数的调用结果),那么编译器会在编译时计算出结果。 -
运行时求值:如果调用
constexpr函数时,传递给它的参数不是常量表达式(例如运行时才能确定的变量),那么函数将像普通的内联函数一样在运行时被调用。
constexpr 函数的优点:
- 性能提升:对于在编译时就能确定的计算,避免了运行时的开销。
-
可以用在常量表达式中:这是
constexpr最重大的用途之一,允许在需要常量表达式的地方使用函数调用的结果。 -
编译时检查:如果
constexpr函数被期望在编译时求值,但由于参数不是常量表达式而无法做到,编译器可能会给出警告或错误。
constexpr 函数的例子:
#include <iostream>
#include <array>
// C++11 版本的 constexpr 函数,只能包含一个 return 语句
constexpr int power(int base, int exp) {
return (exp == 0) ? 1 : base * power(base, exp - 1);
}
// C++14 版本的 constexpr 函数,可以包含更复杂的逻辑
constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
int main() {
// 在编译时求值:
constexpr int compileTimePower = power(2, 5);
std::cout << "编译时计算的 2 的 5 次方: " << compileTimePower << std::endl;
// 可以在需要常量表达式的地方使用:
std::array<int, power(3, 2)> myArray; // 数组大小在编译时确定
// 在运行时求值(如果参数不是常量表达式):
int runtimeBase = 3;
int runtimeExp = 4;
int runtimePower = power(runtimeBase, runtimeExp);
std::cout << "运行时计算的 3 的 4 次方: " << runtimePower << std::endl;
constexpr int compileTimeFactorial = factorial(5);
std::cout << "编译时计算的 5 的阶乘: " << compileTimeFactorial << std::endl;
return 0;
}
与 const 和 inline 的区别:
-
const: 用于声明常量变量,表明变量的值在初始化后不能被修改。 -
inline: 是一个函数提示符,提议编译器将函数体直接插入到调用的地方以减少函数调用的开销,但这只是一个提议,编译器可能会忽略。inline函数依旧是在运行时进行求值的。 -
constexpr: 用于声明函数或变量,表明其值可以在编译时确定。constexpr函数可以在编译时或运行时求值,而constexpr变量的值必须在编译时确定。
constexpr 函数是 C++ 中一个强劲的特性,它允许函数在编译时进行求值,从而提高性能并支持在需要常量表达式的场景中使用。constexpr 函数需要满足一些限制,例如函数体只能包含特定的语句,并且只能调用其他 constexpr 函数或特定的内置函数。一个 constexpr 函数既可以在编译时求值(如果参数是常量表达式),也可以在运行时求值(如果参数不是常量表达式)。
想象一下,你正在做数学题。
-
constexpr函数就像是一个超级机智的计算器,它可以在你真正需要答案之前就把一些题目提前算好。 -
例子:
- 老师问你:“2 乘以 5 等于多少?” 你很快就能回答是 10。这个就像电脑在编译时就能算出来。
- 如果老师问你:“如果今天来了 10 个小朋友,每人发 3 颗糖,一共需要多少颗糖?” 你也能很快算出来是 30。
-
constexpr函数就是告知电脑:“嘿,如果我知道你需要哪些数字,我就能提前把答案算好,这样你真正运行程序的时候就更快了!”
我们可以画一个场景:
[电脑] --> [机智的计算器 (在电脑里面)] --> [答案]
- 画一台电脑,电脑里面有一个特别机智的计算器。
- 告知小朋友,这个机智的计算器可以在电脑真正开始工作之前,提前计算一些简单的数学题。
目前,我们用更接近代码的语言,但依旧保持简单的方式来解释:
-
constexpr int multiplyByTwo(int number) { return number * 2; }-> “告知电脑,这是一个叫做 ‘multiplyByTwo’ 的小工具。如果你给它一个数字,它会帮你把这个数字乘以 2。而且,如果它一开始就知道这个数字是多少,它就能提前算出答案。”-
constexpr-> “表明这是一个很机智的工具,可以提前计算” -
int-> “表明这个工具会给你一个整数(数字)作为答案” -
multiplyByTwo-> “这是这个工具的名字” -
(int number)-> “你需要给这个工具一个整数作为输入,我们叫它 ‘number’ ” -
{ return number * 2; }-> “这是这个工具的工作方式:把输入的数字乘以 2,然后告知你结果”
-
-
如果我们在写程序的时候就告知电脑一个固定的数字,列如
constexpr int result = multiplyByTwo(5);,那么电脑在真正运行程序之前就能算出result是 10。 -
但是,如果我们在程序运行的时候才给
multiplyByTwo一个数字(列如从用户那里输入),那么电脑就只能在程序运行的时候再计算答案了。




















暂无评论内容