TLDR

  1. 修饰变量的时候,可以把 constexpr 对象当作加强版的 const 对象:const 对象表明值不会改变,但不一定能够在编译期取得结果;constexpr 对象不仅值不会改变,而且保证能够在编译期取得结果。如果一个 const 变量能够在编译期求值,将其改为 constexpr 能够让代码更清晰易读。
  2. constexpr 函数可以把运行期计算迁移至编译期,使得程序运行更快(但会增加编译时间)。但如果 constexpr 函数中存在无法在编译期求值的参数,则 constexpr 函数和普通一样在运行时求值,此时的返回值不是常量表达式。

1. 常量表达式和 constexpr

C++11 中引入了 constexpr 关键字。constexpr 是 const expression 的缩写,即常量表达式

常量表达式是指值不会改变编译期可以得到结果的表达式。

1.1 特点

  1. 值不会改变(这一点和普通 const 一样)
  2. 编译期就能得到结果!(普通 const 不一定保证)

1.2 使用场景

C++ 在一些场景下必须使用常量表达式,比如:

  • 数组大小
  • 整型模板实参(如 std::array<T, N> 的长度参数 N)
  • switch-case 中的 case 标签
  • 枚举量的值
  • 对齐规格

1.3 常见的常量表达式

  1. 字面值(如 42)
  2. 用常量表达式初始化的 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 的好处

  1. 编译器可以保证 constexpr 对象是常量表达式(能够在编译期取得结果),而 const 对象不能保证。如果一个 const 变量能够在编译期求值,将其改为 constexpr 能够让代码更清晰易读
  2. constexpr 函数可以把运行期计算迁移至编译期,使得程序运行更快(但会增加编译时间)

对于常量表达式(编译期值已知),编译器可以进行更多优化,比如放到只读内存中。但这并不是 constexpr 特有的,有的 const 变量也是常量表达式

1.7 小结

  1. 修饰对象的时候,可以把 constexpr 当作加强版的 const:const 对象只表明值不会改变,不一定能够在编译期取得结果;constexpr 对象不仅值不会改变,而且保证能够在编译期取得结果
  2. constexpr 函数既可以用于编译期计算,也可以作为普通函数在运行期使用

扩展阅读

  • 《C++ Primer 第五版》p58,p214,p267

  • 《Effective Modern C++》条款 15:只要有可能使用 constexpr,就使用它

一文总结 C++ 常量表达式、constexpr 和 const的更多相关文章

  1. 常量表达式 & constexpr

    [常量表达式] 一个这样的表达式:值不会改变 && 在编译过程中就能够得到计算结果 常见的常量表达式:字面值.用常量表达式初始化的const对象 一个对象是不是常量表达式由它的数据类型 ...

  2. 第8课 常量表达式(constexpr)

    一. const 和constexpr的区别 (一)修饰变量时,const为“运行期常量”,即运行期数据是只读的.而constexpr为“编译期”常量,这是const无法保证的.两者都是对象和函数接口 ...

  3. constexpr和常量表达式

    常量表达式:值不会改变并且在编译过程就能得到计算结果的表达式. 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式. 一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同 ...

  4. 常量表达式和constexpr(c++11)

    常量表达式 常量表达式是指值不会改变且在编译阶段就能得到计算结果的表达式(两点要求) ; //是常量表达式 ; //是常量表达式 "; const int siz=s.size(); //不 ...

  5. const限定符、constexpr和常量表达式------c++ primer

    编译器将在编译过程中把用到const变量的地方都替换成对应的值,为了执行这种替换,编译器必须知道变量的初始值.如果程序包含多个文件,则那个用了const对象的文件都必须能访问到它的初始值才行.要做到这 ...

  6. 10、初识constexpr和常量表达式

    常量表达式:是指值不会改变并且在编译过程就能得到计算结果的表达式.显然字面值属于常量表达式,用于表达式初始化的const对象也是常量表达式. 1.判断一个变量是不是常量表达式 一个对象(表达式)是不是 ...

  7. C++常量表达式、const、constexpr(C++11新增)的区别

    常量表达式是指值不会改变且在编译过程中就能够得到计算结果的表达式,能在编译时求值的表达式. 程序先编译再运行:  在编译阶段, 编译器将在编译过程中把用到该常量的地方都全都替换为 常量的值. 但是常量 ...

  8. c++nullptr(空指针常量)、constexpr(常量表达式)

    总述     又来更新了,今天带来的是nullptr空指针常量.constexpr(常量表达式)C++的两个用法.Result result_fun = nullptr;constexpr stati ...

  9. constexpr 和常量表达式

    常量表达式(是const expression) 是指值不会改变并且在编译过程中就能得到计算结果的表达式.显然,字面值属于常量表达式,用常量 表达式初始化的const 对象也是常量表达式.后面将会提到 ...

  10. c++11 常量表达式

    c++11 常量表达式 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inclu ...

随机推荐

  1. 星索称重/生产管理软件 联机版V1.0

    星索称重/生产管理软件 联机版V1.0   一.特点 1.支持多用户.多组织管理,灵活控制用户权限. 2.支持地磅秤.智能电子秤.轨道秤等多款称重设备. 3.支持三联单/热敏纸等多种打印模板. 二.系 ...

  2. 【MCU】浮点数如何判等

    [来源]https://mp.weixin.qq.com/s/481H4imm73IIS1yFI7-DNA

  3. Go-GC-三色标记与混合写屏障

  4. linux环境C语言实现:h265与pcm封装成AVI格式

    ​ 前言 不知道是处于版权收费问题还是什么原因,H265现在也并没有非常广泛的被普及.将h265数据合成AVI的资料现在在网上也基本上没有.使用格式化工厂工具将h265数据封装成AVI格式,发现它在封 ...

  5. [转帖]总成本降低80%,支付宝使用OceanBase的历史库实践

    https://open.oceanbase.com/blog/5377309696 为解决因业务增长引发的数据库存储空间问题,支付宝基于 OceanBase 数据库启动历史库项目,通过历史数据归档. ...

  6. [转帖]oracle 审计日志清理

    https://www.cnblogs.com/bangchen/p/7268086.html   --进入审计日志目录: cd $ORACLE_BASE/admin/$ORACLE_SID/adum ...

  7. [转帖]KV数据库调研

    https://zhuanlan.zhihu.com/p/499313638 Redis作为NoSQL领域的代表,拥有很高的读写性能,支持比较丰富的数据类型,但是Redis也存在一些缺陷. l 内存数 ...

  8. [转帖]Postmark - 存储性能测试工具

    1. 引言 Postmark是由著名的NAS提供商NetApp开发,用来测试其产品的后端存储性能. Postmark主要用于测试文件系统在邮件系统或电子商务系统中性能,这类应用的特点是:需要频繁.大量 ...

  9. [转帖]Windows自带硬盘测试工具使用教程

    本教程主要讲解Windows自带的硬盘测试工具的使用,不用再安装第三方软件了.到底准不准就不知道啦,下面我们来看看如何使用吧~ 1. 进入cmd 快速进入cmd 主要如果进入后,使用命令直接闪退,就是 ...

  10. [转帖]Native Memory Tracking 详解(3):追踪区域分析(二)

    https://www.modb.pro/db/539804 上篇文章 Native Memory Tracking 详解(2):追踪区域分析(一) 中,分享了NMT追踪区域的部分内存类型--Java ...