Scott Meyers在effective modern c++中提到“If there were an award for the most confusing new word in C++11, constexpr would probably win it.”

由此可见,constexpr确实是比较难以让人理解。加之其在C++11和14中的标准略有不同,也加剧了这种难度。

参考几本经典教材(C++ primer, effective modern C++, a tour of C++)以及蓝色大大在知乎上的一些解答,整理出constexpr的用法和注意事项。

1.概念,constexpr objects

C++ primer中给出的定义是 “常量表达式是指不会改变并且在编译过程中就能得到计算结果的表达式 【1】。”

可以理解为在const上又加一层限定条件,即const并不限定是编译期常量还是运行期常量,而constexpr必须是编译期常量(在编译阶段得到结果)。

举例如下:

众所周知,array的size是需要在编译期确定的,所以当其size不是一个常量表达式时,是无法通过编译的。

int i;
const int size = i;
int arr[size]; //error,size不是常量表达式,不能在编译期确定

而如果size是一个constexpr变量,则符合编译期确定的条件,可以通过编译。

constexpr auto size = ;
int arr[size]; //OK,size时常量表达式

当然,要定义一个常量表达式的时候,也要确保其右侧是常量表达式,否则该处便无法通过编译。

int i;
constexpr int size = i; // error,i不能在编译期确定

所以用effective modern c++中的一句话总结这一部分就是:

“constexpr objects are const and are initialized with values known during compilation【2】”.

2. constexpr functions

比起constexpr变量,用constexpr修饰的函数有些更容易混淆的地方。

1) constexpr修饰的函数,当传入参数是可以在编译期计算出来时,产生constexpr变量;

             当传入参数不可以在编译期计算出来时,产生运行期遍历(constexpr等于不存在)。

因此,不必写两个函数,如果函数体存在constexpr适用条件,就应该加上constexpr关键字。

例如(例子来源【3】):

constexpr int foo(int i) {
return i + ;
} int main() {
int i = ;
std::array<int, foo()> arr; // OK,5是常量表达式,计算出foo(5)也是常量表达式 foo(i); // Call is Ok,i不是常量表达式,但仍然可以调用(constexpr 被忽略) std::array<int, foo(i)> arr1; // Error,但是foo(i)的调用结果不是常量表达式了 }

2) 在C++11和14中的区别

在C++11标准中,对于constexpr修饰的函数给了及其苛刻的限定条件:函数的返回值类型及所有形参的类型都是字面值类型,而且函数体内必须有且只有一条return语句【1】

这个条件显然是太苛刻了,以至于很多在constexpr的操作都要借助?:表达式,递归等办法实现。

在C++14中,放宽了这一限定,只保留了“函数的返回值类型及所有形参的类型都是字面值类型”,也就是说,这些值都在编译期能确定了就行。

3. constexpr class(字面值常量类)

built-in类型是字面值常量,但是有时需要自定义类型也作为字面值常量,这时候就需需要将constexpr修饰构造函数。

字面值常量类必须至少提供一个constexpr构造函数。

例如:

class Point {
public:
constexpr Point(double xval = , double yval = ): x(xval), y(yval) { }
constexpr double getX() const {return x;}
constexpr double getY() const {return y;}
private:
double x,y;
};

当这样定义一个类后,便可以将Point类型的对象定义为字面值常量。即:

constexpr Point p1(9.4, ,);
constexpr Point p2(28.8, 5.3); constexpr
Point midpoint(const Point& p1, const Point& p2) {
return {p1.getX() + p2.getX() / , p1.getY() + p2.getY() / } ;
} constexpr auto mid = midpoint (p1, p2);

上述例子中,p1,p2均为字面值常量,midpoint为constexpr修饰的函数,所以求取mid的整个过程均在编译期就可以完成,软件运行的时间自然会大大减少。

至此关于constexpr的三个主要用途(constexpr变量,constexpr修饰函数,constexpr修饰构造函数)就总结完毕,下面是一些注意事项。

注意事项1: 很多人(包括我自己)在gcc中验证数组大小必须在编译期指定的例子时发现:

如果array定义在主函数内,即使给定的不是一个常量表达式,也可以通过编译。这差点颠覆了我的认知。。。

蓝色大大在知乎答案【4】中解释了这一点,其实是C99中的variable length array。在全局变量中不能使用(无法分配内存),在局部变量中可以使用,细节可以参考那份解答。

注意事项2:constexpr这么复杂,到底为什么要用?

其实第一还是为了效率。效率是C++的设计哲学之一,编译期可以确定的东西,便可以提醒编译期优化,也可能存放在read-only memory中

第二就是这样声明的constexpr变量便可以用在诸如上述数组长度指定,还有包括模板参数,case标签等场合,会便于使用【5】

参考资料:

1. Stanley B. Lippman / Josée Lajoie / Barbara E. Moo,  C++ Primer 中文版(第 5 版)[M].  电子工业出版社,2013

2. Meyers S. Effective Modern C++[M]. O'Reilly, 2014.

3. 蓝色在知乎问题“C++ const 和 constexpr 的区别?”中的解答: https://www.zhihu.com/question/35614219

4. 蓝色在知乎问题“constexpr和const数组的区别?”中的解答: https://www.zhihu.com/question/29662350/answer/45192834

5. Stroustrup B. A Tour of C++[M]. Addison-Wesley Longman, Amsterdam, 2013.

constexpr:编译期与运行期之间的神秘关键字的更多相关文章

  1. c++ 编译期与运行期

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 转自:http://h ...

  2. 深入分析Java的编译期与运行期

    不知大家有没有思考过,当我们使用IDE写了一个Demo类,并执行main函数打印 hello world时都经历了哪些流程么? 想通过这篇文章来分析分析Java的执行流程,或者换句话说想聊聊Java的 ...

  3. Java编译期与运行期

    编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程.在Java中也就是把Java代码编成class文件的过程.编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本 ...

  4. c++ 之 编译期多态&运行期多态

    编译时多态:程序运行前发生的事件 —— 函数重载.运算符重载 .模板  ——静态绑定 运行时多态:程序运行时发生的事件 —— 虚函数机制——动态绑定 template<typename T> ...

  5. 深入理解Java虚拟机读书笔记7----晚期(运行期)优化

    七 晚期(运行期)优化 1 即时编译器(JIT编译器)     ---当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”,包括被多次调用的方法和被多次执行的循环体.     ...

  6. java在编译期和运行期都做了什么

    Java对象内存存储,引用传递,值传递详细图解 java对象在内存中的分配 编译过程: 编译器把一种语言规范转化为另一种语言规范的这个过程需要哪些步骤?回答这个问题需要参照<编译原理>,总 ...

  7. Javac早期(编译期)

    从Sun Javac的代码来看,编译过程大致可以分为3个过程: 解析与填充符号表过程. 插入式注解处理器的注解处理过程. 分析与字节码生成过程. Javac编译动作的入口是com.sun.tools. ...

  8. JVM总结(六):早期(编译期)优化

    这节我们来总结一下JVM编译器优化问题. JVM编译器优化 Javac编译器 Javac的源码和调试 解析与填充符号表 注解处理器 语法分析与字节码生成 Java语法糖 泛型和类型擦除 自动装箱.拆箱 ...

  9. 深入理解JVM - 早期(编译期)优化

    Java“编译期”是一段“不确定”的操作过程:可能是指一个前端编译器(编译器的前端)把*.java文件转变为*.class文件的过程:可能是指虚拟机的后端运行期编译器(JIT编译器,Just In T ...

随机推荐

  1. 关于JavaScript的43道题①

    最近在github上大火的43到js代码题,有很多人搬运.原链接https://github.com/lydiahallie/javascript-questions 1.下面代码的输出是什么? fu ...

  2. description The request sent by the client was syntactically incorrect.

    shi用url传递参数,其他页面都ojbk的 唯独有一个请求 不应该啊 这明显的已经配置好了啊 因为别的请求我也是这样配置的啊,运行是没问题的啊 运行好好的啊 一时间,感觉无从下手了 经过十几分钟各种 ...

  3. 在Vmware安装虚拟机WindowsServer 2003

    一.创建并安装虚拟机 新建Windows2003server系统 按照下面操作即可 https://www.cnblogs.com/color-blue/p/8525710.html 二.安装虚拟机 ...

  4. oracle创建定时任务

    一.dmbs_job dbms_job涉及到的知识点 1.创建job: variable jobno number; dbms_job.submit(:jobno, —-job号  'your_pro ...

  5. js点击复制文本

    // 动态创建 input 元素 var aux = document.createElement("input"); // 获得需要复制的内容 aux.setAttribute( ...

  6. 洛谷 P1951 收费站_NOI导刊2009提高(2) 最短路+二分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 思路 AC代码 总结 题面 题目链接 P1951 收费站_NOI导刊2009提高(2) 其 ...

  7. 模拟15 题解(waiting)

    T1 60%算法 定义f[i][j]表示枚举到i位置,已经使用过了j个队, $f[i][j]+=f[i-1][t] ( t \in [max(0,j-k),j])$滚动一下 这是个O(n^3)的,考虑 ...

  8. 高维护性的javascript

    养成良好的编码习惯,提高代码的可维护性 避免定义全局变量或函数 定义全局的变量和函数,会影响代码的可维护性.如果在页面中运行的javascript 代码是在相同的作用域里面,那就可能代码之间存在互相影 ...

  9. Android消息机制使用注意事项,防止泄漏

    在Android的线程通信当中,使用频率最多的就是Android的消息处理机制(Handler.send().View.post().Asynctask.excute()等等都使用到了消息处理机制). ...

  10. 第二周<岭回归>

    传统最小二乘法缺乏稳定性 额.就是曾加正则项 \( argmin||Xw-y||^2+\alpha||w||^2 \) 对应矩阵的求解方法为 \(w=(X^TX+\alpha*I)^{-1}X^Ty\ ...