当x是Const int[]时,X[0]==1‘常数表达式C++11?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (18)

下面的C ++ 11程序是否格式不正确?

const int x[] = {1,2,3};

static_assert(x[0] == 1, "yay");

int main() {}

gcc和clang似乎都这么认为,但为什么不是x[0] == 1一个常量表达式?

x[0] == 1
subscript operator
*(x+0) == 1
array-to-pointer conversion (int* p = x)
*(p+0) == 1
pointer addition
*p == 1
indirection (lvalue y = x[0])
y == 1
lvalue-to-rvalue conversion:

一个非易失性的glvalue(yes,x [0]是一个glvalue和non-volatile)的整型(是的,它的类型是const int)或者是指向一个非易失性const对象的枚举类型(是的,它的类型为const int)与前面的初始化(是初始化为1),用一个常量表达式进行初始化(是1是常量表达式)

看起来是正确的,x数组的第一个元素满足这些条件。

1 == 1

?

这是编译器错误,标准缺陷,还是我错过了什么?

5.19 [expr.const]的哪部分表示这不是一个常量表达式?

提问于
用户回答回答于

按照标准的当前措辞,这是一个编译器错误,并且该程序格式良好。现在正在考虑它是否应该是一个标准缺陷,因为实施起来会很困难。

报告复制如下:

C ++ 11官方目前的措辞直到N3690包含以下内容: 条件表达式e是核心常量表达式,除非e的评估将评估以下表达式之一:

  • 一个左值到右值的转换(4.1),除非它适用于
    • 整数或枚举类型的非易失性glvalue,它引用具有前面初始化的非易失性常量对象,用常量表达式进行初始化

    全球范围内的以下声明: const int x[2] = {42, 43}; 定义一个2个const int对象的数组,列表初始化为 {42, 43} 在8.5.1 [dcl.init.aggr] / 2中: 当按照8.5.4的规定初始化器列表对初始化器列表进行初始化时,初始化器列表的元素将作为聚合器成员的初始化器,以下标或成员顺序递增。 所以第一个元素对象42的初始值设定项是,第二个元素对象的初始值设定项是43。 该表达式*x是一个左值和一个核心常量表达式。它需要一个数组到指针的转换和一个间接 - 它们都不会使表达式成为一个核心常量表达式。glvalue表达式指的是第一个元素对象x*x是整数类型(const int)的非易失性glvalue,它引用具有前面初始化的非易失性常量对象,并用常量表达式初始化42。 因此,*x 在常量表达式中允许应用于glvalue的左值到右值转换,因此以下内容格式良好: constexpr int y = *x;

用户回答回答于

在5.19:

除非涉及以下其中一项,否则表达式是一个常量表达式:

  • 一个左值到右值的转换(4.1),除非它适用于
    • 一个整数或枚举类型的glvalue,它引用一个具有前面初始化的非易失性const对象,用一个常量表达式进行初始化,或者
    • 一个字面类型的glvalue,它是指用constexpr定义的非易失性对象,或者是指这样一个对象的子对象,或者
    • 一个文字类型的glvalue,它是指用一个常量表达式初始化的非易失性临时对象

简单地说,左值到右值的转换只能在常量表达式中完成,如果:

  • 一个用常量初始化的常量整型(或枚举)声明:const int x = 3;
  • 以下声明constexprconstexpr int x[] = {1,2,3};
  • 一个用常量表达式初始化的临时对象...

你的例子包括左值到右值的转换,但没有这些例外,所以x不是一个常量表达式。但是,如果你将其更改为:

constexpr int x[] = {1,2,3};

static_assert(x[0] == 1, "yay");

int main() {}

然后一切都很好。

扫码关注云+社区