常量表达式是什么_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的意义是冲突的。

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 海量数据处理分析

    海量数据处理分析 笔者在实际工作中,有幸接触到海量的数据处理问题,对其进行处理是一项艰巨而复杂的任务。原因有以下几个方面:一、数据量过大,数据中什么情况都可能存在。如果说有10条数据,那么大不了每条去逐一检查,人为处理,如果有上百条数据,也可以考虑,如果数据上到千万级别,甚至过亿,那不是手工能解决的了,必须通过工具或者程序进行处理,尤其海量的数据中,什么情况都可能存在,例如,数据中某处格式出了问题

  • BeanCopier类[通俗易懂]

    BeanCopier类[通俗易懂]网上学习了一番BeanCopier类。cglib是一款比较底层的操作java字节码的框架。下面通过拷贝bean对象来测试BeanCopier的特性:publicclassOrderEntity{privateintid;privateStringname;//Gettersandsettersareom…

  • h5 iframe嵌套页面_汇总IOS下奇葩BUG以及iframe嵌套页面带来的一些困扰

    h5 iframe嵌套页面_汇总IOS下奇葩BUG以及iframe嵌套页面带来的一些困扰做H5开发,安卓和IOS的兼容问题经常会困扰我们,尤其是跟第三方平台合作,用到iframe嵌入式应用,令很多Web前端开发的童鞋脑壳疼,相信大家也入了不少坑,且踩且珍惜吧,呵呵^_^。今天抽时间整理一些出来,希望能帮助到大家。iframe自动变宽了,在IOS手机上出现滚动条第一步:定义iframe中的scrolling属性为no,设置iframe中不显示滚动条。第二步:设置iframe的样式为…

  • RabbitMQ使用教程(超详细)

    推荐springCloud教程:https://blog.csdn.net/hellozpc/article/details/83692496推荐Springboot2.0教程:https://blog.csdn.net/hellozpc/article/details/82531834文章目录RabbitMQ实战教程1.什么是MQ2.RabbitMQ2.1.RabbitMQ的简介2.2.官…

  • export添加环境变量不生效_如何添加环境变量里面的路径

    export添加环境变量不生效_如何添加环境变量里面的路径export命令功能说明:设置或显示环境变量。语  法:export[-fnp][变量名称]=[变量设置值]补充说明:在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该此登陆操作。参  数:   -f 代表[变量名称]中为函数名称。 -n 删除指定的变量。变量实际上…

  • 关于Visual Studio 2019的前期详情

    关于Visual Studio 2019的前期详情

发表回复

您的电子邮箱地址不会被公开。

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