constexpr

时间:2024-10-21 07:20:13

constexptr和常量表达式

常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。

  • 编译过程中得到计算结果

  • 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

  • 一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定

    • (注意!!!)对于这条语句:const int sz = get_size();,sz本身是常量,但它的具体值直到运行时才能获得,不是常量表达式。
#include<iostream>

using namespace std;

int main(int argc, char* argv[]) {
	const int max_files = 20;          //max_files是常量表达式
	const int limit = max_files + 1;   //limit是常量表达式
	int staff_size = 27;               //staff_size不是常量表达式,数据类型只是普通类型而非常量类型
	const int sz = get_size();         //重要!!!sz本身是常量,但它的具体值直到运行时才能获得,不是常量表达式
}

constexpr变量

  • 在一个复杂的系统中,很难分辨一个初始值到底是不是常量表达式。当然可以定义一个const变量并把它的初始值设为我们认为的某个常量表达式,但在实际使用时,尽管要求如此却常常发现初始值并非常量表达式的情况(例如上述的const int sz = get_size(); 语句)。

  • C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化

#include<iostream>

using namespace std;

int main(int argc, char* argv[]) {
	constexpr int max_files = 20;          //20是常量表达式
	constexpr int limit = max_files + 1;   //max_files + 1是常量表达式
	constexpr int sz = size();         //重要!!!只有当size是一个constexpr函数时,才是一条正确的声明语句
}
  • 尽管不能使用普通函数作为constexpr变量的初始值,但可以通过constexpr函数(编译时就可以计算其结果)初始化constexpr变量。

constexpr函数

  • constexpr函数是指用于常量表达式的函数。

  • 遵循以下规定:(1)函数的返回类型以及所有形参的类型都得是字面值类型;(2)函数体中必须只有一条return语句。

#include<iostream>

using namespace std;

constexpr int new_sz() {
	return 42;
}

int main(int argc, char* argv[]) {
	constexpr int foo = new_sz();
	cout << foo << endl;  //42
	getchar();
	return 0;
}
  • 分析:我们把new_sz定义成无参的constexpr函数。因为编译器能在程序编译时验证new_sz函数返回的是常量表达式,所以可以用new_sz函数初始化constexpr类型的变量foo。

  • 执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了在编译过程中随时展开,constexpr函数被隐式地指定为内联函数

  • constexpr函数体内也可以含有其他语句,只要这些语句在运行时不执行任何操作就行(求解答?)。例如,constexpr函数中可以有空语句、类型别名以及using声明

constexpr int size() {  //返回值类型为字面值类型
	; //空语句
	using In = int; //using声明
	typedef int INT; //类型别名
	return 10;
}

  • 我们允许constexpr函数的返回值并非一个常量。当scale的实参是常量表达式,它的返回值也是常量表达式,反之则不然:
#include<iostream>

using namespace std;

constexpr int size() {
	;
	using namespace std;
	typedef int INT;
	return 10;
}

constexpr int new_sz() {
	return 42;
}

//如果arg是常量表达式,则scale(arg)也是常量表达式
constexpr size_t scale(size_t cnt) {
	return new_sz()*cnt;
}

int main(int argc, char* argv[]) {
	int arr[scale(2)];  //正确
	//int i = 2;
	//int arr2[scale(i)];  //错误:scale(i)不是常量表达式
	const int j = 2;
	int arr3[scale(j)];  //正确
	getchar();
	return 0;
}
  • 把内联函数和constexpr函数放在头文件中:

    • 和其他函数不一样,内联函数和constexpr函数可以在程序中多次定义。毕竟,编译器要想展开函数仅有函数声明是不够的,还需要函数的定义。不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中。

指针和constexpr

  • 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关
	const int *p = nullptr; //p是一个指向整型常量的指针
	constexpr int *q = nullptr;  //q是一个指向整型的常量指针
  • 分析:p和q的类型相差甚远,p是一个指向常量的指针,q是一个常量指针,其中关键在于constexpr把它所定义的对象置为顶层const