以下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(是的,x [0]是glvalue和非易失性)的整数(是的,它有类型const int)或枚举类型引用一个非易失性的const对象(是的它有类型const int)带有前面的初始化(yes用1初始化),用常量表达式初始化(yes 1是常量表达式)
似乎是真的,x
数组的第一个元素满足这些条件。
1 == 1
这是编译器错误,标准缺陷,还是我遗漏了什么?
5.19 [expr.const]的哪一部分说这不是一个常量表达式?
答案 0 :(得分:11)
在5.19中:
表达式是一个常量表达式,除非它涉及以下[...]之一:
左值 - 右值转换(4.1),除非它适用于
- 整数或枚举类型的glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化,或
- 一个文字类型的glvalue,它指的是用constexpr定义的非易失性对象,或者引用的 到这样一个对象的子对象,或
- 一个文字类型的glvalue,它引用一个用常量初始化的非易失性临时对象 表达
明确地说,左值到右值的转换只能在以下情况下用常量表达式完成:
const int x = 3;
初始化的常量整数(或枚举)声明。constexpr
的声明:constexpr int x[] = {1,2,3};
。您的示例包含左值到右值的转换,但没有这些异常,因此x
不是常量表达式。但是,如果您将其更改为:
constexpr int x[] = {1,2,3};
static_assert(x[0] == 1, "yay");
int main() {}
然后一切都很好。
答案 1 :(得分:4)
根据标准的当前措辞,这是一个编译器错误,程序格式正确。现在正在考虑它是否应该是一个标准缺陷,因为它很难实现。
有关详细说明,请参阅:
https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-discussion/Nv5_2dCHm6M
以下复制的报告:
目前C ++ 11官方的措辞到N3690包括在内 有以下内容:
条件表达式e是核心常量表达式,除非e的求值将评估以下表达式之一:
- 左值 - 右值转换(4.1),除非它适用于
- 一个非整数或枚举类型的非易失性glvalue,它引用具有前面初始化的非易失性const对象, 用常量表达式初始化
全球范围内的以下声明:
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
是一个 引用a的整数类型(const int)的非易失性glvalue 具有先前初始化的非易失性const对象,和 使用常量表达式42
初始化。因此,应用于glvalue
*x
的左值到右值转换 允许在常量表达式中,以下是 合式:constexpr int y = *x;
gcc或clang trunk目前都不接受此作为常量 表达,尽管它根据标准形成良好。
这是打算吗?
完整的演示程序:
const int x[2] = {42, 43}; constexpr int y = *x; int main() {}
实现同样失败,使用等效的左值
x[0]
好。