C++ const和constexpr

发布时间 2023-12-19 11:33:19作者: 3的4次方

C++ const和constexpr

C++98 const和C++11 consexpr都是修饰符,即在编译器进行编译的过程中,给编译器一些“要求”或“提示”,但修饰符本身并不产生任何实际代码。

以下描述中,「不可变」=「常量」

const

C/C++ 基础中的基础: const 修饰符用法总结! - 知乎 (zhihu.com)

C++98 const表示初始化后不可变的语义。这里的“初始化”发生的时机不确定,因为const 变量的初始化可以延迟到运行时。事实上在编译的时候,编译器大概率会将完成初始化的const常量出现的地方直接替换为该常量的值,类似于宏替换,取决于编译优化等级。

  • 使用const修饰的变量,用于说明该变量只允许读,不允许改变值。
  • 使用const修饰的对象,用于说明该对象只允许读,不允许改变(改变任何成员变量的值,调用任何非const成员函数)。
  • 使用const修饰的成员函数,用于说明该函数不修改该对象成员的值。
  • 使用const修饰的形参,用于说明该形参在函数体内不可变,至于其实参在函数外怎么样不关心。
const int a; // a是一个只读的变量

顶层const

C++干货系列——顶层const和底层const - 知乎 (zhihu.com)

constexpr

C++的const语义 - 知乎 (zhihu.com)

C++11 constexpr表示编译后不可变的语义。这里的“编译后不可变”指的是编译时能确定值,从而运行时不可变,因为constexpr 变量必须在编译时进行初始化

  • 使用constexpr修饰的变量,用于说明变量的值在编译期就能计算出来,此后运行时不再改变。
  • 使用constexpr修饰的函数,用于说明函数的返回值在编译期就能计算出来,此后运行时不再改变。
consexpr int b; // b是一个常量,且是编译器常量,可以在编译时就算出来

常量表达式

常量表达式是说一个表达式的值不会被改变,且在编译期(即静态)就能获得这个表达式的值。相对应的,一个表达式是否是常量表达式就取决于这两个方面:类型(是否const)和初始化方式(编译期是否能拿到值)。

常量表达式函数:编译器会将constexpr函数视为内联函数!所以在编译时若能求出其值,则会把函数调用替换成结果值。

  1. 函数体尽量只包含一个return语句,多个可能会编译出错;
  2. 函数体可以包含其他语句,但是不能是运行期语句,只能是编译期语句;
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的目的是:

  1. 区分「初始化后不可变」和「编译后不可变」这两种语义。
  2. 将没有必要运行时计算的表达式提前到编译期计算。