constexpr 的来龙去脉
constexpr 是什么?
关键字 constexpr
(constant expression) 是在 C++11 中引入的,并且在 C++14 中进行了优化。
constexpr
和 const
一样可以用来修饰变量:试图修改 constexpr
变量时,编译器将会报错。
不同于 const
, constexpr
还可以修饰函数和类的构造函数。 constexpr
表示值或者返回值是常量,并且如果可能,在编译时计算它们。
一个 constexpr
整型值能够用在任何 const
整型值可以用的地方,例如模板参数和数组的申明。
当值在编译时计算而不是运行时计算时,它能够使程序运行得更快,并使用更少的内存。
为了限制编译时常量计算的复杂性,以及其对编译时间潜在的影响, C++14 标准需要 constexpr
类型必须为字面值类型。
语法
constexpr
literal-type identifier = constant-expression ;
constexpr
literal-type identifier { constant-expression } ;
constexpr
literal-type identifier ( params ) ;
constexpr
ctor ( params ) ;
参数
params
一个或者多个参数, 每个参数必须是字面值类型并且本身是 常量表达式 。
返回值
一个 constexpr
变量或者函数必须返回字面值类型。
constexpr
变量
constexpr
和 const
的主要区别是 const
变量的初始化可以被延时到运行时,而 constexpr
变量必须在编译时初始化。所有的 constexpr
变量都是 const
。
当变量由字面值类型初始化时,能够声明为
constexpr
。如果初始化时由构造函数执行的,那么该构造函数也必须声明为constexpr
。引用类型可以声明为
constexpr
只要满足:引用对象是由 常量表达式初始化的,并且初始化时任何隐式转换也是常量表达式。所有
constexpr
变量或者函数的声明必须拥有constexpr
说明符。
举例:
constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression
constexpr
函数
constexpr
函数的返回值是在编译时计算的。调用代码需要返回值在编译时初始化一个 constexpr
变量, 或者提供一个非类型模板参数。 当参数是 constexpr
值时,constexpr
函数产生一个编译时的常量。当被调用时传入非 constexpr
参数,或者其返回值在非编译时请求,constexpr
函数和普通函数一样将产生提个运行时的值。(这种行为能够让你避免编写两个相同功能的函数,一个为 constexpr
版本,一个为非 constexpr
版本)
constexpr
函数或者构造函数默认是 inline
的.
以下规则适用于 constexpr
函数:
- 必须接受并返回字面值类型。
- 可以是递归的.
- 不能是虚构的。当类有虚基类时,构造函数不能定义为
constexpr
。 - 函数体可以定义为
= default
或者= delete
。 - 函数体不能包含
goto
语句或者try
块。 - 显式特化/具体化(explicit specialization)的非
constexpr
模板能够声明为constexpr
。 - 显式特化/具体化(explicit specialization)的非
constexpr
模板不必声明为constexpr
。
以下 constexpr
函数规则适用于 Visual Studio 2017 及以后的版本:
- 可以包含
if
和switch
语句,以及所有循环语句包括for
,while
和 do-while - 可以包含初始化的局部变量,并且必须是字面值类型,不能是
static
或者 thread-local。该局部变量不必是const
的。 - 非
static
的constexpr
成员函数不必隐式为const
constexpr float exp(float x, int n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
}
提示:
在 Visual Studio 调试器中, 当调试一个非优化的调试版本,你能够通过在函数内部设置断点来区分
constexpr
函数是否在编译时计算。如果断点能够触发,则为运行时计算,否则,为编译时计算。
示例
以下例子演示了 constexpr
变量, 函数, 以及用户自定义类型。 在 main()
最后, constexpr
成员函数 GetValue()
是在运行时调用的,因为其返回值没有被要求在编译时确定。
#include <iostream>
using namespace std;
// Pass by value
constexpr float exp(float x, int n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
}
// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp2(x * x, n / 2) :
exp2(x * x, (n - 1) / 2) * x;
}
// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
return N;
}
// Recursive constexpr function
constexpr int fac(int n)
{
return n == 1 ? 1 : n * fac(n - 1);
}
// User-defined type
class Foo
{
public:
constexpr explicit Foo(int i) : _i(i) {}
constexpr int GetValue() const
{
return _i;
}
private:
int _i;
};
int main()
{
// foo is const:
constexpr Foo foo(5);
// foo = Foo(6); //Error!
// Compile time:
constexpr float x = exp(5, 3);
constexpr float y { exp(2, 5) };
constexpr int val = foo.GetValue();
constexpr int f5 = fac(5);
const int nums[] { 1, 2, 3, 4 };
const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };
// Run time:
cout << "The value of foo is " << foo.GetValue() << endl;
}
constexpr 和 const 的异同
相同处:
constexpr
和 const
一样可以用来修饰变量:试图修改 constexpr
变量时,编译器将会报警。一个 constexpr
整型值能够用在任何 const
整型值可以用的地方,例如模板参数和数组的申明。
不同处:
constexpr
还可以修饰函数和类的构造函数。constexpr
** 表示值或者返回值是常量,并且如果可能,在编译时计算它们。
当一个值在编译时计算而不是运行时计算时,它能够使程序运行得更快,并使用更少的内存。
所有的 constexpr
对象都是 const
的,但不是所有的 const
对象都是 constexpr
的。
示例
int sz; // non-constexpr variable
…
constexpr auto arraySize1 = sz; // error! sz's value not
// known at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a
//compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2
// is constexpr
int sz; // as before
…
const auto arraySize = sz; // fine, arraySize is
// const copy of sz
std::array<int, arraySize> data; // error! arraySize's value
// not known at compilation
应该使用 constexpr 的场景
只要允许,尽可能使用 constexpr
,当值在编译时计算而不是运行时计算时,它能够使程序运行得更快,并使用更少的内存。
不应该使用 constexpr 的场景
constexpr
是对象或者函数接口的一部分,所以如果你使用了 constexpr
但反悔了,移除 constexpr
可能会导致大量的调用代码编译失败。(比如添加 I/O 操作用于调试或者性能调优可能导致这样的问题,因为 I/O 语句通常不是在 constexpr
函数中执行的。)
引用
Microsoft Docs (https://docs.microsoft.com/en-us/cpp/cpp/constexpr-cpp?view=msvc-160)
Effective Modern C++
constexpr 的来龙去脉的更多相关文章
- C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)
#include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...
- [c++] constexpr and literal class
稀奇古怪的新特性,菜鸟在此啄上一啄. 1. When should literal classes be used in C++? 2. int i; // not constant const ...
- 【转载】VC维的来龙去脉
本文转载自 火光摇曳 原文链接:VC维的来龙去脉 目录: 说说历史 Hoeffding不等式 Connection to Learning 学习可行的两个核心条件 Effective Number o ...
- Sql Server来龙去脉系列 必须知道的权限控制核心篇
最近写了<Sql Server来龙去脉系列 必须知道的权限控制基础篇>,感觉反响比较大.这可能也说明了很多程序猿对数据库权限控制方面比较感兴趣,或者某些技术点了解的没有很透彻. 有些人看 ...
- Sql Server来龙去脉系列 必须知道的权限控制基础篇
题外话:最近看到各种吐槽.NET怎么落寞..NET怎么不行了..NET工资低的帖子.我也吐槽一句:一个程序猿的自身价值不是由他选择了哪一门技术来决定,而是由他自身能创造出什么价值来决定. 在进入本篇内 ...
- Sql Server来龙去脉系列之四 数据库和文件
在讨论数据库之前我们先要明白一个问题:什么是数据库? 数据库是若干对象的集合,这些对象用来控制和维护数据.一个经典的数据库实例仅仅包含少量的数据库,但用户一般也不会在一个实例上创建太多 ...
- Sql Server来龙去脉系列之三 查询过程跟踪
我们在读写数据库文件时,当文件被读.写或者出现错误时,这些过程活动都会触发一些运行时事件.从一个用户角度来看,有些时候会关注这些事件,特别是我们调试.审核.服务维护.例如,当数据库错误出现.列数据被更 ...
- Sql Server来龙去脉系列之二 框架和配置
本节主要讲维持数据的元数据,以及数据库框架结构.内存管理.系统配置等.这些技术点在我们使用数据库时很少接触到,但如果要深入学习Sql Server这一章节也是不得不看.本人能力有限不能把所有核心的知识 ...
- Sql Server来龙去脉系列之一 目录篇
从工作一直到现在都没怎么花功夫深入学习下Sql Server数据库,在使用Sql Server时90%的时间基本上都是在接触T-SQL,所以数据库这块基本上属于菜鸟级别.至于数据库的底层框架以及运行机 ...
随机推荐
- 【EXP】根据字段导出数据query
exp有些时候需要根据字段来进行导出操作 例如:想要导出hr用户中的employees中salary要大于4000的数据 这样的话需要添加where语句,需要用到的参数是query 查看下大于4000 ...
- 设计一款兼容ST207和GD207的开发板
在MCU的学习中,大部分人都是学习别人的开发板,例如正点原子.野火等,优点是有可靠的教程和代码,缺点是容易让人有种自己全部都学会的了错觉,听了课程编写了代码,运行正常. 这个时候,可以尝试自已做一块属 ...
- 24v转3.3v稳压芯片,高效率DC-DC变换器3A输出电流
PW6206系列是一个高精度,高输入电压低静态电流,高速,低功耗降线性稳压器具有高纹波抑制.输入电压高达40V,负载电流为在VOUT=5V和VIN=7V时高达300mA.该设备采用BCD工艺制造.PW ...
- day132:2RenMJ:MJ需求文档&MJ游戏流程&Egret白鹭引擎安装&TypeScript简要介绍
目录 1.麻将产品需求文档 2.麻将游戏流程 3.Egret白鹭引擎 4.TypeScript简要了解 5.TypeScript快速入门 1.麻将产品需求文档 1.麻将术语 1.名词术语 牌⼦: 序数 ...
- 删除HDFS中指定的文件。
1 import java.text.SimpleDateFormat; 2 import java.util.Scanner; 3 4 import org.apache.hadoop.fs.Fil ...
- V2版的接口在V3版里面都能找到对应接口 数据结构
开发文档 - 微信支付商户平台 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api.shtml 版本选择 关闭 V2版接口和V3版接口实际 ...
- 【LinuxShell】ps 命令浅析
前言 Linux上查看进程状态最常用的命令,本文对 ps 命令参数以及状态做一下简单介绍. 参数 ps a 显示现行终端机下的所有程序,包括其他用户的程序. ps -A 显示所有程序. ps c 列出 ...
- 洛谷P3413 P6754
双倍经验题 由于我先做的 P6754,所以一切思路基于 P6754 的题目 " P6754 这题就是 P3413 的究极弱化版 " --By Aliemo. P6754 Descr ...
- LOJ10074架设电话线
USACO 2008 Jan. Silver 在郊区有 N 座通信基站,P 条双向电缆,第 i 条电缆连接基站 Ai 和 Bi.特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中.现在, ...
- jackson学习之五:JsonInclude注解
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...