常量表达式是什么_const常量

常量表达式是什么_const常量常量表达式值(constant-expressionvalue)。通常情况下,常量表达式值必须被一个常量表达式赋值,而跟常量表达式函数一样,常量表达式值在使用前必须被初始化。一、常量表达式1.1运行时常量性与编译时常量性在C++中,我们常常会遇到常量的概念。常量表示该值不可修改,通常是通过const关键字来修饰的。比如:constinti=3;const还可以修饰函数参数、函数返回值、函数本身、类等。在不同的使用条件下,const有不同的意义,不过大多数情况下,const描述的都

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

常量表达式值(constant-expression value)。通常情况下,常量表达式值必须被一个常量表达式赋值,而跟常量表达式函数一样,常量表达式值在使用前必须被初始化。

一、常量表达式

1.1 运行时常量性与编译时常量性

在C++中,我们常常会遇到常量的概念。常量表示该值不可修改,
通常是通过const关键字来修饰的。比如:

const int i = 3;

const还可以修饰函数参数、函数返回值、函数本身、类等。在不同的使用条件下,const有不同
的意义,不过大多数情况下,const描述的都是一些“运行时常量性”的概念,即具有运行时数据的不可更改性。不过有的时候,我们需要的却是编译时期的常量性,这是const关键字无法保证的。

#include <iostream>
#include <string>
using namespace std;

const int GetConst() { 
    return 1; }
void Constless(int cond) { 
   
int arr[GetConst()] = { 
   0}; // 无法通过编译
enum { 
    e1 = GetConst(), e2 }; // 无法通过编译
switch (cond) { 
   
case GetConst(): // 无法通过编译
break;
default:
break;
}
}

int main()
{ 
   
    cout<< GetConst()<< endl;
}

bash-4.4$ g++ constexpr.cpp -o expr && ./expr
constexpr.cpp: In function ‘void Constless(int)’:
constexpr.cpp:8:21: error: call to non-‘constexpr’ function ‘const int GetConst()’
 enum { 
    e1 = GetConst(), e2 }; // 无法通过编译
             ~~~~~~~~^~
constexpr.cpp:8:21: error: call to non-‘constexpr’ function ‘const int GetConst()’
constexpr.cpp:8:22: error: enumerator value for ‘e1’ is not an integer constant
 enum { 
    e1 = GetConst(), e2 }; // 无法通过编译
                      ^
constexpr.cpp:10:14: error: call to non-‘constexpr’ function ‘const int GetConst()case GetConst(): // 无法通过编译
      ~~~~~~~~^~
constexpr.cpp:10:14: error: call to non-‘constexpr’ function ‘const int GetConst()

我们定义了一个返回常数1的函数GetConst。我们使用了const关键字修饰了返回类型。不过编译后我们发现,无论将GetConst的结果用于需要初始化数组Arr的声明中,还是用于匿名枚举中,或用于switch-case的case表达式中,编译器都会报告错误。发生这样错误的原因如我们上面提到的一样,这些语句都需要的是编译时期的常量值。而const修饰的函数返回值,只保证了在运行时期内其值是不可以被更改的。这是两个完全不同的概念。

1.2 如何获得编译期常量

简单粗暴的做法 :使用C中的宏替代GetConst函数。

#define GetConst 1

C++11中对编译时期常量的回答是constexpr,即常量表达式(constant expression)。

constexpr int GetConst() { 
    return 1; }

即在函数表达式前加上constexpr关键字即可。有了常量表达式这样的声明,编译器就可以在编译时期对GetConst表达式进行值计算(evaluation),从而将其视为一个编译时期的常量(虽然编译器不一定
这么做,但至少从语法效果上看是这样,我们会在后面叙述)。

#define GetConst 1

#include <iostream>
#include <string>
using namespace std;

#define GetConst 1

//const int GetConst() { return 1; }

void Constless(int cond) { 
   
int arr[GetConst] = { 
   0}; 
enum { 
    e1 = GetConst, e2 }; 
switch (cond) { 
   
case GetConst:
    cout<< GetConst <<endl;
break;
default:
break;
}
}

int main()
{ 
   
    //cout<< GetConst()<< endl;
    Constless(1);
}

constexpr

#include <iostream>
#include <string>
using namespace std;

constexpr int GetConst() { 
    return 1; }
void Constless(int cond) { 
   
int arr[GetConst()] = { 
   0};
enum { 
    e1 = GetConst(), e2 };
switch (cond) { 
   
case GetConst():
break;
default:
break;
}
}

int main()
{ 
   
    cout<< GetConst()<< endl;
}

2.1 常量表达式函数

通常我们可以在函数返回类型前加入关键字constexpr来使其成为常量表达式函数。不过并非所有的函数都有资格成为常量表达式函数。事实上,常量表达式函数的要求非常严格,总结起来,大概有以下几点:
·函数体只有单一的return返回语句。
·函数必须返回值(不能是void函数)。
·在使用前必须已有定义。
·return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式

首先是常量表达式函数中最为明显的限制,就是要求函数体中只有一条语句,且该条语句必须是return语句。
这就意味着形如:

constexpr int data() { 
    const int i = 1; return i; }

这样的多条语句的写法是无法通过编译的。不过一些不会产生实际代码的语句在常量表达式函数中使用下,倒不会导致编译器的“抱怨”。我们可以看看如下static_assert的情况:

constexpr int f(int x){ 
   
static_assert(0 == 0, "assert fail.");
return x;
}

该例子能够通过编译。而其他的,比如using指令、typedef等也通常不会造成问题。

第二点约束,则是常量表达式必须返回值。形如constexpr void f(){}这样的不返回值的函数就不能是常量表达式。当然,其原因也很明显,因为无法获得常量的常量表达式是不被认可的。
第三点约束是常量表达式函数在使用前必须被定义。对于普通函数而言,调用函数只需要有函数声明就够了,但常量表达式函数的使用则有所不同。这里读者应该注意常量表达式“使用”和“调用”的区别,前者
讲的是编译时的值计算,而后者讲的是运行时的函数调用

constexpr int f();
int a = f();
const int b = f();
constexpr int c = f(); //无法通过编译 f先声明,此时还没有实现
constexpr int f() { 
    return 1; }
constexpr int d = f();

在a和b的定义中,编译器会将f()转换为一个函数调用,
而在c的定义中,由于其是一个常量表达式值,因此会要求编译器进行编译时的值计算。
这时候由于f常量表达式还没有定义,就会导致编译错误。
而d的定义则没有问题,因为f的定义已经有了。

第四点非常重要,常量表达式中,也不能使用非常量表达式的函数。形如

const int e(){ 
    return 1;}
constexpr int g(){ 
    return e(); }

或者形如

int g = 3;
constexpr int h() { 
    return g; }

的常量表达式定义是不能通过编译的。这样做的意义也比较明显,即如果我们要使得g()是一个编译时的常量,那么其return表达式语句就不能包含运行时才能确定返回值的函数。只有这样,编译器才能够在编译时进行常量表达式函数的值计算。
当然,作为一个常量表达式函数,return的表达式需要是一个常量表达式也是天经地义的事情。一些危险的操作,比如赋值的操作在常量表达式中也是不允许的,形如

constexpr int k(int x) { 
    return x = 1; }

的语句也是无法通过C++11编译器的编译的。

3.1 常量表达式值

常量表达式值(constant-expressionvalue)。通常情况下,常量表达式值必须被一个常量表达式赋值,而跟常量表达式函数一样,常量表达式值在使用前必须被初始化。而使用constexpr声明的数据最常被问起的问题是,下列两条语句有什么区别:

const int i = 1;
constexpr int j = 1;

事实上,两者在大多数情况下是没有区别的。不过有一点是肯定的,就是如果i在全局名字空间中,编译器一定会为i产生数据。而对于j,如果不是有代码显式地使用了它的地址,编译器可以选择不为它生成数据,而仅将其当做编译时期的值(是不是想起了光有名字没有产生数据的枚举值,以及不会产生数据的右值字面常量?事实上,它们也都只是编译时期的常量)。

而对于自定义类型的数据,要使其成为常量表达式值的话,则不像内置类型这么简单。C++11标准中,constexpr关键字是不能用于修饰自定义类型的定义的。比如下面这样的类型定义和使用:

constexpr struct MyType { 
   int i; }
constexpr MyType mt = { 
   0};

在C++11中,就是无法通过编译的。正确地做法是,定义自定义常量构造函数(constent-expression constructor)。

struct MyType { 
   
constexpr MyType(int x): i(x){ 
   }
int i;
};
constexpr MyType mt = { 
   0};

代码清单6-4中,我们对MyType的构造函数进行了定义。不过在定
义前,我们加上了constexpr关键字。通过这样的定义,MyType类型的
constexpr的变量mt的定义就可以通过编译了。
常量表达式的构造函数也有使用上的约束,主要的有以下两点:
·函数体必须为空。
·初始化列表只能由常量表达式来赋值。

在C++11中,不允许常量表达式作用于virtual的成员函数。这个原因也是显而易见的,
virtual表示的是运行时的行为,与“可以在编译时进行值计算”的constexpr的意义是冲突的。

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

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

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


相关推荐

  • intellij idea2022 激活码【2022免费激活】2022.02.07

    (intellij idea2022 激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年4月1日
    145
  • wireshark抓包分析——TCP/IP协议[通俗易懂]

    wireshark抓包分析——TCP/IP协议[通俗易懂]本文来自网易云社区当我们需要跟踪网络有关的信息时,经常会说“抓包”。这里抓包究竟是什么?抓到的包又能分析出什么?在本文中以TCP/IP协议为例,简单介绍TCP/IP协议以及如何通过wireshark抓包分析。Wireshark是最著名的网络通讯抓包分析工具。功能十分强大,可以截取各种网络封包,显示网络封包的详细信息。Wireshark下载安装,略。注意,若在Windows系统安装Wireshar…

    2022年9月7日
    0
  • linux smartctl 命令,Smartctl 命令查看硬盘详细信息

    linux smartctl 命令,Smartctl 命令查看硬盘详细信息Smartctl命令查看硬盘详细信息(2011-08-3014:21:41)标签:linux硬盘信息使用时间杂谈1.1什么是Smartmontools?Smartmontools是一种硬盘检测工具,通过控制和管理硬盘的SMART(SelfMonitoringAnalysisandReportingTechnology,自动检测分析及报告技术)技术来实现的,SMART技术可以对硬盘的磁头单…

    2022年6月17日
    50
  • CTF-UPX脱壳加壳讲解;(详细版)

    CTF-UPX脱壳加壳讲解;(详细版)在做CTF-RE题的时候,下载的题目附件会发现缺少函数方法的现象,说明这个文件就被加壳处理了;这个是加壳状态下的;脱壳后~~~~~~~如何发现是加壳的呢?除了开头所描述的方法,还有第二种用ExeinfoPE软件查看附件信息;此时这个软件就提示我们这个附件是UPX加壳处理的;二.脱壳这里我只讲一种方法(因为我只会一种方法-.-)首先下载好打包好的UPX脱壳工具,解压下载好:讲一下用法吧在这个文件夹当中输入cmd进入;输入upx.exe-h有如下反应:

    2022年7月19日
    68
  • chkdsk命令修复磁盘没反应_chkdsk怎么修复c盘

    chkdsk命令修复磁盘没反应_chkdsk怎么修复c盘故障笔记本电脑进不了系统,XP系统的开机进度条一走完蓝屏画面一闪就自己重启了,试了安全模式与最后一次正确配置都是一样的情况,据笔记本的主人说想一键还原也不行(装有GHOST一键还原)。使用PE工具盘启动笔记本,进入“我的电脑”一看,C盘信息全没了,没有已用空间大小,也没有可用空间大小,连C盘大小也没有了,格式变成RAW,但是用工具盘上的DiskGenius却可以看到里面的文件,也可以看到C盘是NT…

    2022年10月27日
    0
  • JavaScript匿名函数理解及应用[通俗易懂]

    JavaScript匿名函数理解及应用[通俗易懂]匿名函数匿名函数顾名思义就是没有名字的函数,在实际开发中经常会用到,也是JavaScript的重点。匿名函数又叫立即执行函数。由于这种函数是匿名的,所以它不能被调用。由于它不能被调用,所以如果它不立即执行的话就没有了意义。由于它需要立即执行,所以在执行完之后匿名函数就会被销毁。匿名自执行函数的作用就是用于闭包和创建独立的命名空间两个方面。匿名函数的基本形式为(function(){…})();前面的括号包含函数体,后面的括号就是给匿名函数传递参数然后立即执行。匿名函数的作用是避免全局变量

    2022年10月4日
    0

发表回复

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

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