定义
- C++11新增语法
- 编译器常量,在编译器求值
constexpr
函数或变量必须满足一定条件才能在编译期求值。如constexpr
只能进行简单操作(算术运算、逻辑运算) ,不能包含运行时才能确定的操作,如内存分配、文件读取
与const
对比
以前的const修饰符并不能保证产生编译期常量,因此某些用法,比如用const常量声明数组长度不能保证可行。相比之下,constexpr类型具有更强的编译期检查,可以在编译阶段就发现常量能否在编译阶段确定
example
- 无法初始化
std::vector
、std::list
- 以
std::vector
为例,它在内部管理着一块动态分配的连续内存区域来存储元素。在创建std::vector
对象时,它会根据需要分配内存,这个过程涉及到运行时的内存分配操作,无法在编译期完成 - 例如,
constexpr std::vector<int> v = {1, 2, 3};
会报错。因为std::vector
的构造函数需要在运行时确定内存分配的大小和位置,即使提供了初始化列表,也无法在编译期确定这些内存相关的操作。 - 代替方案 :可以考虑使用
std::array
。std::array
是一个固定大小的数组容器,它的大小在编译期是确定的,可以用于替代std::vector
在编译期常量场景下的部分功能。例如,constexpr std::array<int, 3> a = {1, 2, 3};
是合法的
- 以
- 无法初始化
std::string
、QString
std::string
是一个类类型,它的构造涉及到动态内存分配 (用于存储字符串内容)等运行时操作。例如,当创建一个std::string
对象时,它会在堆上分配内存来存储字符序列,这个过程是在运行时发生的,无法在编译期完成- C++标准库在设计时没有为
std::string
提供constexpr
构造函数。在 C++20 之前,标准库中的大部分容器类都不能使用constexpr
构造函数 - 代替方案 :可以使用
const char*
或const char[]
,形如constexpr std::string str = "hello";
- 代替方案 :
std::string_view
(C++17)是一个轻量级的字符串视图类,它不拥有字符串的内容,只是提供了对字符串内容的查看功能 。在某些情况下,可以将std::string_view
作为编译期常量来处理字符串- 不过需要注意的是,
std::string_view
本身只是一个视图,如果要将其转换为std::string
,仍然可能涉及到运行时操作,如复制字符串内容到新的std::string
对象中。但在只需要查看字符串内容而不涉及修改和存储的场景下,std::string_view
可以作为一种编译期常量来使用
- 不过需要注意的是,
- 无法初始化输入/输出(I/O)操作的类型(如
std::ofstream
、std::ifstream
等)std::ofstream
(用于输出文件流)和std::ifstream
(用于输入文件流)这样的类型,它们的操作本质上依赖于外部设备(如硬盘上的文件)和运行时的系统调用 。例如,当打开一个文件流时,需要在运行时查找文件系统中的文件、获取文件句柄等操作,这些操作无法在编译期完成。如constexpr std::ofstream out("output.txt");
是非法的,因为在编译期无法执行打开文件并建立输出流的操作。- 替代方案 :如果只是需要在编译期确定一些文件相关的常量信息,比如文件名,可以使用
constexpr const char*
来表示文件名。例如,constexpr const char* filename = "output.txt";
,然后在运行时使用这个常量来打开文件流,如std::ofstream out(filename);
- 无法初始化大多数自定义类(如果其构造函数涉及非
constexpr
操作)-
对于自定义类,如果其构造函数包含运行时操作,如动态内存分配、调用非
constexpr
函数、等待用户输入等,那么这个类就不能在编译期初始化 。例如,假设有一个自定义类MyClass
,其构造函数如下:cppclass MyClass { public: MyClass() { // 动态分配内存 data = new int; } private: int* data; };
对于这样的类,
constexpr MyClass obj;
是不合法的,因为其构造函数中的动态内存分配操作无法在编译期完成 -
替代方案 :要使自定义类能够在编译期初始化,需要确保其构造函数中的操作都是可以在编译期完成的。需要修改构造函数,避免动态内存分配等运行时操作。例如,可以将上述
MyClass
的构造函数修改为使用静态存储的成员变量,或者使用constexpr
函数来初始化成员变量等方式,使其符合编译期初始化的要求
-