C++ const和constexpr
C++98 const和C++11 consexpr都是修饰符,即在编译器进行编译的过程中,给编译器一些“要求”或“提示”,但修饰符本身并不产生任何实际代码。
以下描述中,「不可变」=「常量」
const
C++98 const
表示初始化后不可变的语义。这里的“初始化”发生的时机不确定,因为const 变量的初始化可以延迟到运行时。事实上在编译的时候,编译器大概率会将完成初始化的const常量出现的地方直接替换为该常量的值,类似于宏替换,取决于编译优化等级。
- 使用
const
修饰的变量,用于说明该变量只允许读,不允许改变值。 - 使用
const
修饰的对象,用于说明该对象只允许读,不允许改变(改变任何成员变量的值,调用任何非const成员函数)。 - 使用
const
修饰的成员函数,用于说明该函数不修改该对象成员的值。 - 使用
const
修饰的形参,用于说明该形参在函数体内不可变,至于其实参在函数外怎么样不关心。
const int a; // a是一个只读的变量
顶层const
C++干货系列——顶层const和底层const - 知乎 (zhihu.com)
constexpr
C++11 constexpr
表示编译后不可变的语义。这里的“编译后不可变”指的是编译时能确定值,从而运行时不可变,因为constexpr 变量必须在编译时进行初始化。
- 使用
constexpr
修饰的变量,用于说明变量的值在编译期就能计算出来,此后运行时不再改变。 - 使用
constexpr
修饰的函数,用于说明函数的返回值在编译期就能计算出来,此后运行时不再改变。
consexpr int b; // b是一个常量,且是编译器常量,可以在编译时就算出来
常量表达式
常量表达式是说一个表达式的值不会被改变,且在编译期(即静态)就能获得这个表达式的值。相对应的,一个表达式是否是常量表达式就取决于这两个方面:类型(是否const)和初始化方式(编译期是否能拿到值)。
常量表达式函数:编译器会将constexpr函数视为内联函数!所以在编译时若能求出其值,则会把函数调用替换成结果值。
- 函数体尽量只包含一个return语句,多个可能会编译出错;
- 函数体可以包含其他语句,但是不能是运行期语句,只能是编译期语句;
const int x = 10;
const int fun1() { return 1; } //非常量表达式函数
constexpr int fun2() { int y = x + 2; return y; } //常量表达式函数
int main() {
//以下不是常量表达式
{
int a = 1;
const int b = fun1();
//constexpr int c = a;//错误
//constexpr int d = fun1();//错误
}
//以下是常量表达式
{
const int a = 1;
constexpr int b = a;
constexpr int c = fun2();
}
}
从以上例子中可以看出,由于const
语义表达运行时的含义,但未必编译时能确定其值,因此实际上constexpr
的约束更强。
总结
提出constexpr的目的是:
- 区分「初始化后不可变」和「编译后不可变」这两种语义。
- 将没有必要运行时计算的表达式提前到编译期计算。