C 14中的§5.19/3定义了积分常量表达式和转换常量表达式:
整数常量表达式是整数或无作用域枚举类型的表达式,隐式转换为prvalue,其中转换的表达式是核心常量表达式。[注意:此类表达式可用作数组边界(8.3.4, 5.3.4),作为位字段长度(9.6),如果基础类型不固定,则可用作枚举器初始化器(7.2),并用作对齐(7.6.2)。-结束注释]类型T
的转换常量表达式是一个表达式,隐式转换为类型T
的prvalue,其中转换后的表达式是核心常量表达式,隐式转换序列仅包含用户定义的转换、左值-右值转换(4.1)、整数提升(4.5)和整数转换(4.7),而不是缩小转换(8.5.4)。[注意:此类表达式可以在new
表达式(5.3.4)中使用,作为case
表达式(6.4.2),如果底层类型是固定的,则作为枚举器初始值设定项(7.2),作为html" target="_blank">数组边界(8.3.4),以及作为整数或枚举非类型模板参数(14.3)。-结束注释]
也许我错过了什么,但我的第一印象是每个积分常量表达式都是一个转换的常量表达式。
编辑
我还认为这一段有一个错误:
而不是:
T类型的转换常量表达式是一个表达式,隐式转换为T类型的prvalue,...
应该是:
T型转换常量表达式是一个表达式,隐式转换为整型prvalue
此更改允许编译以下代码:
#include <iostream>
struct A { operator int() { return 5; } } a;
int main() {
int b[a]{ 0, 1, 2, 3, 4 };
std::cout << b[4] << '\n';
}
其中
a
在声明intb[a]{0,1,2,3,4}中
是类型为a
的转换常量表达式,隐式转换为整型(int
)的prvalue,其中转换表达式5
是核心常量表达式,隐式转换序列仅包含用户定义的转换。
在讨论了Jerry Coffin和cpplearner提供的答案后,让我提议重写那些该死的规则,如下所示:
[expr.const] 5.20\3(修改)
整型常量表达式是整型或非作用域枚举类型的表达式,它隐式转换为同一类型的prvalue核心常量表达式,从而隐式转换序列仅包含左值到右值的转换。
[expr.const]5.20\4(已修改)
类型T的转换常量表达式是任何类型的表达式,该表达式隐式转换为类型T的常量表达式,从而隐式转换序列仅包含:
std::nullptr\u t
,std::nullptr\u t
和以及引用绑定(如果有)直接绑定的位置。[注:此类表达式可用于<代码>新 表达式中,如大小写表达式、枚举器初始值设定项(如果基础类型是固定的)、数组边界和非类型模板参数。-结束注]
现在区别很明显,呃?还应该提醒的是,根据5.20\7; 4\5,在某些情况下可以使用文字类类型的表达式来代替积分常量表达式。
注意:此答案基于目前最新的标准草案,称为N4567。指出了它与C 11/14标准之间的一些差异。
就类类型而言,整数常量表达式和转换后的常量表达式是不同的。在C 98/03中,当类类型不能在这里使用时(因为当时没有constexr
转换函数),确实没有T
类型的转换常量表达式这样的术语。
对于整数常量表达式,目标类型未知。但对于类型T
的转换常量表达式,目标类型已知为T
,并且T
不一定是整数或无作用域的枚举类型1。
因此,为了编译整型常量表达式,编译器首先需要确定目标类型。如果表达式具有整型或非作用域枚举类型,那么显然目标类型就是表达式的类型。否则,如果表达式有一个文本类类型(让我们称这个类型为E
),则使用以下过程2:
编译器检查E
3中的所有非显式转换函数。假设这些函数的结果类型形成一个集合s
。如果S
只包含一个整型或非作用域枚举类型(参考修饰符被去除,const
和volatile
限定符被忽略:const volatile int
(需要注意的是,在上述过程中,不会检查转换函数模板。)
因此,例如,如果一个类类型有两个转换函数,一个是
constepr operator int
,另一个是constepr operator long
,则该类型不能在整型常量表达式中使用(目标类型是不可判定的)。然而,这种类型可以用在int
类型的转换常量表达式中,或者用在long
类型的转换常量表达式中。
在确定目标类型
D
后,应用重载解析以找到最合适的转换函数或函数模板,然后调用choosen转换函数(必须是constexpr
)以生成类型D
-这部分或多或少与类型为D
的转换常量表达式相同。
在下面的示例中,
Var{}
是一个有效的整型常量表达式,但它是一个无效的转换常量表达式,类型为std::size_t
(该示例受此问题启发)。
class Var
{
public:
constexpr operator int ()
{ return 42; }
template <typename T>
constexpr operator T () = delete;
};
enum {
x = Var{} // the initializer of `x` is expected to be an
// integral constant expression
// x has value 42
};
int t[ Var{} ]; // the array bound is expected to be a
// converted constant expression of type std::size_t
// this declaration is ill-formed
N4567 5.20[expr.const]第7页
如果在需要整型常量表达式的上下文中使用文字类类型的表达式,则该表达式将在上下文中隐式转换(第4条)为整型或无范围枚举类型,且所选转换函数应为
constexpr
。
N4567 4[conv]p5
某些语言结构需要转换为一个值,该值具有适合该结构的特定类型集之一。在这样的上下文中出现的类类型
e
的表达式e
被称为上下文隐式转换为特定类型T
,并且当且仅当e
可以隐式转换为类型T
时,其格式良好,该类型确定如下:e
搜索返回类型为cv的非显式转换函数T
或引用cvT
,以便上下文允许T
。只有一个这样的T
。
在C 11/14中,转换后的常量表达式只能是整数或枚举类型。N4268改变了这一点
在C 11中,没有这样的过程,而是要求“类类型应具有一个到整数或枚举类型的非显式转换函数,且转换函数应为constepr
”N3323将其更改为当前的措辞
C 14标准中没有“不明确”一词。它是由CWG 1981年添加的
这两个定义都是必要的,因为你可以用一个做一些事情,但不能用另一个。不,不是每个整数常量表达式都是真正的转换常量表达式。举个明显的例子,转换后的常量表达式禁止缩小转换,但整数常量表达式不允许。
因此我不能这样做:
enum x : char { a = 1024 };
然而,如果枚举的初始值设定项允许使用整型常量表达式,而不是转换后的常量表达式,那么这恰恰是允许的。
作为维恩图,我会把情况画成这样:
因此,两者之间有相当多的重叠(可能比这张图所暗示的要多),但每一个都允许至少一些其他不允许的事情。我已经给出了一个例子,说明了每个方向上的一个项目,但没有尝试详尽地列出差异。
不过,我并不完全相信整型常量表达式禁止用户定义的转换(快速测试表明,我手头的编译器允许用户定义的转换)。这将给出我最初写下这个答案时的情况,更像这样:
常量表达式机制是为了: 提供一种更加通用的常量表达式 允许用户自定义的类型成为常量表达式 提供了一种保证在编译期完成初始化的方法(可以在编译时期执行某些函数调用) 考虑下面这段代码: enum Flags { good=0, fail=1, bad=2, eof=4 }; constexpr int operator|(Flags f1, Flags f2) { return Flags(int(
问题内容: 因此,我正在研究具有一些静态常量的此类: 然后,我想一种基于常量获取相关字符串的方法: 但是,当我编译时,在3个大小写标签的每一个上都出现错误。 我知道编译器需要在编译时就知道表达式才能编译开关,但是为什么不是常量? 问题答案: 尽管从初始化字段之后执行的任何代码的角度来看,它们是恒定的,但从JLS 的角度来看,它们不是编译时常数。有关常量表达式1的规范,请参见第15.28节常量表达式
第 2 章 常量、变量和表达式 目录 1. 继续Hello World 2. 常量 3. 变量 4. 赋值 5. 表达式 6. 字符类型与字符编码
问题内容: 我的转换案例声明昨天运行良好。但是,当我今天早些时候运行代码时,eclipse给我一个错误,强调了红色的case语句,并说:case表达式必须是常量表达式,它是常量,我不知道发生了什么。这是我的代码如下: 所有R.id.int都用红色下划线。 问题答案: 在常规的Android项目中,资源R类中的常量声明如下: 但是,从ADT 14开始,在图书馆项目中,它们将这样声明: 换句话说,常数
问题内容: 我有以下代码 我知道根据JLS,只允许将常量表达式作为批注属性的值。但为什么?如果数据类型匹配,为什么还不够?如果要在运行时对表达式进行求值,是否有可能出错?每个规范背后都有逻辑推理吗? 问题答案: 注释就像类型扩展或有关该类型的元数据。 因为Java是一种静态类型的语言(意味着类型在编译时是已知的),所以在编译时也知道注释属性数据(元数据)似乎是合理的- 您正在定义/声明有关注释(扩
整型字面值,例如42,就是常量表达式。所以,简单的数学表达式,例如,23x2-4。可以使用其来初始化const整型变量,然后将const整型变量作为新表达的一部分: const int i=23; const int two_i=i*2; const int four=4; const int forty_two=two_i-four; 使用常量表达式创建变量也可用在其他常量表达式中,有些事只能用