constexpr和常量表达式

constexpr和常量表达式常量表达式常量表达式(constexpression)是指值不会改变并且在编译过程就能得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。constinta=3;//a是常量表达式constintb=a+1;//b是常量表达式intc=8;//c不是常量表达式,因为c的数据类型是int而不是constintco


Jetbrains全系列IDE稳定放心使用

常量表达式

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

const int a = 3;//a是常量表达式
const int b = a+1;//b是常量表达式
int c = 8;//c不是常量表达式,因为c的数据类型是int而不是const int
const int d = get_size();//d不是常量表达式,因为d的值要到运行时才能获取到

字面值类型

常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为字面值类型(literal type)。

字面值类型的概念由Bjarne Stroustrup提出,644号议题(2008.2)将字面值概念的概念定义如下:
    A type is a literal type if it is:
a scalar type; or
a class type with
·a trivial copy constructor,
·a trivial destructor,
·a trivial default constructor or at least one constexpr constructor other than the copy constructor,
all non-static data members and base classes of literal types; or
an array of literal type.
大意是:
一个字面值类型应具有如下特点
是一个标量类型(如整型、浮点型、物理类型和枚举类型)
或是一个具有如下特征的类:
·一个平凡的拷贝构造函数
·一个平凡的析构函数
·一个默认构造函数或者至少一个constexpr类型的构造函数(除拷贝构造函数外),所有非静态的数据成员以及字面值类型的基类
或者一个字面值类型的数组
上面对于字面值类型的定义还牵涉到平凡类型(trivial type),在另一篇博文会讲到。
从定义可以知道,算术类型(整型、浮点型等)、引用、枚举和指针这些简单数据类型都属于字面值类型,此外满足特定条件的类也属于字面值类型。
尽管指针和引用都能定义成constexpr,但它们的初始值却受到严格限制。一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象(如全局变量、静态变量等)。

constexpr变量

在一个复杂的系统中,很难(几乎肯定不可能)分辨一个初始值到底是不是常量表达式。尽管我们可以定义一个const变量并把它的初始值设为我们认为的某个常量表达式,但在实际使用时,尽管要求如此,却常常发现初始值并非常量表达式的情况。因此,对象的定义和使用根本就是两回事儿。

从C++11开始,规定允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。

constexpr int a = 3;//3是常量表达式
constexpr int b = a+1;//b是常量表达式
constexpr int c = get_size();//只有当get_size()是一个constexpr函数时,才是常量表达式,否则语句错误

constexpr指针

需要注意的是,与const关键字不同,一个指针被定义为constexpr,关键字仅对指针有效,与指针所指的对象无关:

const int *p = nullptr;//p是一个指向整型常量的指针
constexpr int *q = nullptr;//q是一个指向整型的常量指针,在这一点上类似于int *const p
指针p和q的类型相差甚远,p是一个指向常量的指针,而q是一个常量指针,其中的关键在于constexpr把它所定义的对象置为了顶层const。
与const指针类似,constexpr指针既可以指向常量也可以指向一个非常量。

int i = 3;
constexpr int *p = &i;

constexpr函数

constexpr函数(constexpr expression)是指能用于常量表达式的函数。定义constexpr函数的方法与其他函数类似,不过要遵循几项约定(根据2008.6的647号议题)。

1.函数体被声明为constexpr
2.非虚函数
3.返回类型及所有形参的类型都必须是字面值类型

4.函数中有且只有一条return语句(在C++14标准中这条规定被删除)

constexpr int new_sz () {return 24;}
constexpr int foo = new_sz();

C++11中关于constexpr函数的定义参考链接:cppreference
在C++14中极大放宽了对constexpr函数的定义限制
执行对constexpr函数的初始化时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中展开,constexpr函数被隐式地指定为内联函数。
特别的,constexpr函数允许返回值并非一个常量:
constexpr size_t scale (size_t cnt) {return new_sz()*cnt}//如果arg是常量表达式,则scale(arg)也是常量表达式
int arr[scale(2)];//正确,scale(2)是常量表达式
constexpr函数不一定返回常量表达式,返回值可以为空(return ;)。

和其它函数不一样,内联函数和constexpr函数可以在程序中多次定义。毕竟,编译器要想展开函数仅有函数声明时不够的,还需要函数的定义。不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中。

constexpr构造函数

尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数。事实上,一个字面值常量类必须至少提供一个constexpr构造函数。
constexpr构造函数可以声明成=default的形式(或者是删除函数的形式)。否则,constexpr构造函数就必须既符合构造函数的要求(意味着不能包含返回语句),又符合constexpr函数的要求(意味着它能拥有的唯一可执行语句就是返回语句)。综合这两点可知,constexpr构造函数体一般来说应该是空的,因此对函数成员的初始化必须放在初始化列表中。

constexpr构造函数必须初始化所有数据成员,constexpr构造函数保证了传递给它的所有参数都是constexpr类型的,产生的对象的所有成员也都是constexpr。



本文部分内容摘自《C++ Primer(第5版)》

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/187828.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 剑指Offer——Trie树(字典树)

    剑指Offer——Trie树(字典树)剑指Offer——Trie树(字典树)Trie树Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

    2022年9月4日
  • SQL server 2008 R2 图文安装教程(附资源)[通俗易懂]

    SQL server 2008 R2 图文安装教程(附资源)[通俗易懂]安装环境及资源系统环境:Windows10Enterprise64位SQL版本:sql_server_2008_r2_enterprise(网盘下载密码:xexg)NETFramework3.5:(网盘下载密码:cvbf)安装过程下载并解压sql_server_2008_r2_enterprise点击setup。若打开后…

    2022年6月23日
  • visual c++2010密钥

    visual c++2010密钥6VPJ7-H3CXH-HBTPT-X4T74-3YVY7

    2022年5月18日
  • java删除linux文件_Java删除文件

    java删除linux文件_Java删除文件//将缓冲文件夹中的文件删除Strings=”D:\\txt\\inBuffer\\”+fileInfo[0]+”\\”+fileID;//文件的绝对路径Filefile=newFile(s);if(file.exists()){booleand=file.delete();if(d){System.out.print(“删除成功!”);}else{System.out…

    2022年6月14日
  • ConcurrentHashMap 1.7和1.8区别

    ConcurrentHashMap 1.7和1.8区别ConcurrentHashMap与HashMap和Hashtable最大的不同在于:put和get两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Segment里面的Entry,然后在遍历entry链表(1)从1.7到1.8版本,由于HashEntry从链表变成了红黑树所以concurrentHashMap的时间复杂度从O(n)到O…

    2022年6月24日
  • 顺丰科技QT面试题「建议收藏」

    顺丰科技QT面试题「建议收藏」自定义控件:应该做过吧?能举几个例子吗?还有其他的吗?你觉得自定义控件的方法主要是哪些?答:从外观设计上:QSS、继承绘制函数重绘、继承QStyle相关类重绘、组合拼装等等从功能行为上:重写事件函数、添加或者修改信号和槽等等QSS:QSS平时使用的多吗?能举几个例子吗?都是如何使用,能说说吗?答:1.将QSS统一写在一个文件中,通过程序给主窗口加载;2.写成一个字符串中,通过程序给主窗口加载;3.需要使用的地方,写一个字符串,加载给对象;4.QTDesigner中填写;事件机制:

    2022年6月25日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号