一文总结 C++ 常量表达式、constexpr 和 const
TLDR
- 修饰变量的时候,可以把 constexpr 对象当作加强版的 const 对象:const 对象表明值不会改变,但不一定能够在编译期取得结果;constexpr 对象不仅值不会改变,而且保证能够在编译期取得结果。如果一个 const 变量能够在编译期求值,将其改为 constexpr 能够让代码更清晰易读。
- constexpr 函数可以把运行期计算迁移至编译期,使得程序运行更快(但会增加编译时间)。但如果 constexpr 函数中存在无法在编译期求值的参数,则 constexpr 函数和普通一样在运行时求值,此时的返回值不是常量表达式。
1. 常量表达式和 constexpr
C++11 中引入了 constexpr 关键字。constexpr 是 const expression 的缩写,即常量表达式。
常量表达式是指值不会改变且编译期可以得到结果的表达式。
1.1 特点
- 值不会改变(这一点和普通 const 一样)
- 编译期就能得到结果!(普通 const 不一定保证)
1.2 使用场景
C++ 在一些场景下必须使用常量表达式,比如:
- 数组大小
- 整型模板实参(如
std::array<T, N>的长度参数 N) - switch-case 中的 case 标签
- 枚举量的值
- 对齐规格
1.3 常见的常量表达式
- 字面值(如 42)
- 用常量表达式初始化的 const 对象
一个对象(或表达式)是否是常量表达式取决于类型和初始值,如:
int i1 = 42; // i1 不是常量表达式:初始值 42 是字面值,但 i1 不是 const 类型
const int i2 = i1; // i2 不是常量表达式:初始值 i1 不是常量表达式
const int i3 = 42; // i3 是常量表达式:用字面值 42 初始化的 const 对象
const int i4 = i3 + 1; // i4 是常量表达式:用常量表达式 i3 + 1 初始化的 const 对象
const int i5 = getValue(); // 如果 getValue() 是普通函数,则 i5 值要到运行时才能确定,则不是常量表达式
1.4 constexpr 变量
上面的例子可以看出,不能直接判断一个 const 对象是否是常量表达式:例如 i4 是否是常量表达式取决于 i3 是否是常量表达式,而 i4 又可能用来初始化其他常量表达式。在复杂的系统中,很难一眼看出某个 const 对象是否是常量表达式。
C++11 允许把变量声明为 constexpr 类型,此时编译器会保证 constexpr 变量是常量表达式(否则编译报错)。换句话说,只要看到 constexpr 类型的变量,则一定能够在编译期取得结果,可以用在需要常量表达式的场景。
int i1 = 42;
constexpr int i2 = i1; // constexpr 变量 'i2' 必须由常量表达式初始化。不允许在常量表达式中读取非 const 变量 'i1'
constexpr int i3 = 42; // i3 是常量表达式
constexpr int i4 = i3 + 1; // i4 是常量表达式
constexpr int i5 = getValue(); // 只有 getValue() 是 constexpr 函数时才可以,否则编译报错
1.5 constexpr 函数
constexpr 函数是指能用于常量表达式的函数。
需要强调的是,constexpr 函数既能用于要求常量表达式/编译期常量的语境,也可以作为普通函数使用。
注意:constexpr 函数不一定返回常量表达式!
只有 constexpr 的所有实参都是常量表达式/编译期常量时,constexpr 函数的结果才是常量表达式/编译期常量。只要有一个参数在编译期未知,那就和普通函数一样,在运行时计算。
constexpr int sum(int a, int b) {
return a + b;
}
constexpr int i1 = 42;
constexpr int i2 = sum(i1, 52); // 所有参数都是常量表达式,sum 的结果也是常量表达式,在编译期求值
int AddThree(int i) {
return sum(i, 3); // i 不是常量表达式,此时 sum 作为普通函数使用
}
为了能保证 constexpr 函数在编译时能随时展开计算,constexpr 函数隐式内联。内联函数和 constexpr 函数不同于其他函数,允许定义多次,但要保证所有的定义一致,所以内联函数和 constexpr 函数一般定义在头文件中。
constexpr 限制
因为需要在编译期求值,所以 constexpr 函数有一些限制:返回类型和所有形参的类型必须是字面值类型(literal type)。
C++11中 constexpr 函数还有一些额外限制(C++14 没有这些限制):
- 返回值类型不能是 void
- 函数体内只能有且只有一条 return 语句(但可以用
? :和递归) - 如果是类的成员函数,则为隐式 const 成员函数
特别说明 constexpr 也可以用于成员函数、甚至构造。
1.6 使用 constexpr 的好处
- 编译器可以保证 constexpr 对象是常量表达式(能够在编译期取得结果),而 const 对象不能保证。如果一个 const 变量能够在编译期求值,将其改为 constexpr 能够让代码更清晰易读
- constexpr 函数可以把运行期计算迁移至编译期,使得程序运行更快(但会增加编译时间)
对于常量表达式(编译期值已知),编译器可以进行更多优化,比如放到只读内存中。但这并不是 constexpr 特有的,有的 const 变量也是常量表达式
1.7 小结
- 修饰对象的时候,可以把 constexpr 当作加强版的 const:const 对象只表明值不会改变,不一定能够在编译期取得结果;constexpr 对象不仅值不会改变,而且保证能够在编译期取得结果
- constexpr 函数既可以用于编译期计算,也可以作为普通函数在运行期使用
扩展阅读
《C++ Primer 第五版》p58,p214,p267
《Effective Modern C++》条款 15:只要有可能使用 constexpr,就使用它
一文总结 C++ 常量表达式、constexpr 和 const的更多相关文章
- 常量表达式 & constexpr
[常量表达式] 一个这样的表达式:值不会改变 && 在编译过程中就能够得到计算结果 常见的常量表达式:字面值.用常量表达式初始化的const对象 一个对象是不是常量表达式由它的数据类型 ...
- 第8课 常量表达式(constexpr)
一. const 和constexpr的区别 (一)修饰变量时,const为“运行期常量”,即运行期数据是只读的.而constexpr为“编译期”常量,这是const无法保证的.两者都是对象和函数接口 ...
- constexpr和常量表达式
常量表达式:值不会改变并且在编译过程就能得到计算结果的表达式. 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式. 一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同 ...
- 常量表达式和constexpr(c++11)
常量表达式 常量表达式是指值不会改变且在编译阶段就能得到计算结果的表达式(两点要求) ; //是常量表达式 ; //是常量表达式 "; const int siz=s.size(); //不 ...
- const限定符、constexpr和常量表达式------c++ primer
编译器将在编译过程中把用到const变量的地方都替换成对应的值,为了执行这种替换,编译器必须知道变量的初始值.如果程序包含多个文件,则那个用了const对象的文件都必须能访问到它的初始值才行.要做到这 ...
- 10、初识constexpr和常量表达式
常量表达式:是指值不会改变并且在编译过程就能得到计算结果的表达式.显然字面值属于常量表达式,用于表达式初始化的const对象也是常量表达式. 1.判断一个变量是不是常量表达式 一个对象(表达式)是不是 ...
- C++常量表达式、const、constexpr(C++11新增)的区别
常量表达式是指值不会改变且在编译过程中就能够得到计算结果的表达式,能在编译时求值的表达式. 程序先编译再运行: 在编译阶段, 编译器将在编译过程中把用到该常量的地方都全都替换为 常量的值. 但是常量 ...
- c++nullptr(空指针常量)、constexpr(常量表达式)
总述 又来更新了,今天带来的是nullptr空指针常量.constexpr(常量表达式)C++的两个用法.Result result_fun = nullptr;constexpr stati ...
- constexpr 和常量表达式
常量表达式(是const expression) 是指值不会改变并且在编译过程中就能得到计算结果的表达式.显然,字面值属于常量表达式,用常量 表达式初始化的const 对象也是常量表达式.后面将会提到 ...
- c++11 常量表达式
c++11 常量表达式 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inclu ...
随机推荐
- SV Interface and Program
内容 验证平台与待测设计的连接 VTB driver和dut之间的连线通过tb中声明wire连线 通过例化dut的方式进行连接 A module的input连接到B module的output SVT ...
- linux-网络状态-netstat
- [转帖]一篇来自网络的关于“enqueue”events的简短参考
https://www.cnblogs.com/lhdz_bj/p/8716701.html 仅供自己和各位同学参考: Enqueue Type Description enq: AD - alloc ...
- 【转帖】nginx变量使用方法详解-8
https://www.diewufeiyang.com/post/582.html 与 $arg_XXX 类似,我们在 (二) 中提到过的内建变量 $cookie_XXX 变量也会在名为 XXX 的 ...
- [转帖]CygWin、MingW、MSYS之间的关系
https://www.jianshu.com/p/09198f6e0a3c 前言 在跨平台开发或移植中,经常会听说Cygwin.MingW.MSYS,他们之间是什么关系?对于将要完成的任务,应该选择 ...
- 【转帖】GPT4All开源的聊天机器人
GPT4All是一个开源的聊天机器人,它基于LLaMA的大型语言模型训练而成,使用了大量的干净的助手数据,包括代码.故事和对话.它可以在本地运行,不需要云服务或登录,也可以通过Python或Types ...
- CPU 大拿的作品
http://nieyong.github.io/wiki_cpu/index.html 改天学习写一下.
- click与addEventListener和removeEventListener事件与移除正确的移除事件详解
1. onclick事件 es5 普通事件就是直接触发事件,相同的事件会被覆盖掉.代码如下: let demoDiv=document.querySelector(".demo") ...
- PHP伪协议与文件包含漏洞
https://www.cnblogs.com/weak-chicken/p/12275806.html 1 file:// - 访问本地文件系统 2 http:// - 访问 HTTP(s) 网址 ...
- 虚拟IP绑定公网IP访问
绑定公网 IP 我们目前的虚拟 IP,还不能通过公网的形式进行访问,我们首先,来使用内部的 IP 进行访问看看效果如下: curl 虚拟IP 如上图我访问了两次,第一次访问返回的是 2222 的 ng ...