【问题标题】:Difference between const and constexpr arraysconst 和 constexpr 数组之间的区别
【发布时间】:2013-08-11 07:40:00
【问题描述】:

为什么constconstexpr 与数组一起使用时会有区别?

int const xs[]{1, 2, 3};
constexpr int ys[]{1, 2, 3};

int as[xs[0]];  // error.
int bs[ys[0]];  // fine.

我希望xs[0]ys[0] 都是常量表达式,但只有后者被这样处理。

【问题讨论】:

  • constexpr 值可以在编译时计算,const 不能。
  • const 数组不需要有可见的定义(它可以是 extern 或者可以在文件的后面定义),这就是存在差异的原因。至于为什么存在 this 差异,我怀疑标准 could 已经说过,当定义存在并且对constexpr 有效时(如您的代码中所示),然后@ 987654331@ 与constexpr 一样好,因为在这种情况下,信息可用于评估它。我不知道为什么它不这么说,但有一个很好的理由,“如果你希望你的数组是constexpr,那么就这么说吧”。
  • 我认为,可能会增加您的问题的后续行动是为什么 int const x = 5;constexpr int y = 5 可以 both 用作数组 decls 的固定大小大小(int a[x], b[y]),但在不同处理的数组内部(参见上面史蒂夫的评论)。
  • @SteveJessop:还是会稍微复杂一些。 constexpr 需要用其他constexpr 初始化,const 数组可以用非constexpr 初始化,比如说函数f()const T array[] = { f(1), f(2), f(3) }; 将是动态初始化,而不是静态(@ 987654343@) 初始化。
  • @DavidRodríguez-dribeas:这就是“定义存在并且对constexpr有效”的意思。就像你说的那样,定义需要能够评估为constexpr,以便进行特殊处理。

标签: c++ arrays c++11 constants constexpr


【解决方案1】:

C++11 constexpr 用于启用在编译时评估表达式,这与 const 关键字不同。

constexpr int ys[]{1, 2, 3}; 在编译时被评估,所以没有错误

当使用ys[0] 时。

另外,请注意这里使用 C++11 统一初始化和 {}

其他例子:

constexpr int multipletwo(int x)
{
return 2*x;
}

int num_array[multipletwo(3)]; //No error since C++11, num_array has 6 elements.

【讨论】:

    【解决方案2】:

    作为社区 wiki 的更长评论。


    表达式xs[0] 在[expr.sub]/1 中定义为*((xs)+(0))。 (请参阅下面的括号。)

    其中一个表达式应具有“指向T”的类型,另一个应具有无作用域枚举或整数类型。

    因此,应用了数组到指针的转换[conv.array]:

    N T 数组”或“T 未知边界数组”类型的左值或右值可以转换为“指向T 的指针”类型的右值。结果是指向数组第一个元素的指针。

    注意它可以对左值进行操作,结果是 prvalue0 因为整数文字也是纯右值。加法在 [expr.add]/5 中定义。由于两者都是 prvalues,因此不需要左值到右值的转换。

    int arr[3];
    constexpr int* p = arr;  // allowed and compiles
    

    现在关键的一步似乎是间接* [expr.unary.op]/1

    一元 * 运算符执行间接:应用它的表达式应该是一个指向对象类型的指针,或者是一个指向函数类型的指针,并且结果是一个左值,该左值指代该对象或函数所指向的对象或函数。表达点。

    所以,xs[0] 的结果是一个左值,引用xs 数组的第一个元素,并且是int const 类型。


    注意[expr.prim.general]/6

    带括号的表达式是主要表达式,其类型和值与括起来的表达式相同。括号的存在不影响表达式是否为左值。


    如果我们现在查看 [expr.const]/2 中不允许某些表达式和转换出现在常量表达式中的项目符号,唯一可以应用的项目符号 (AFAIK) 是左值到右值的转换:

    • 左值到右值的转换 (4.1),除非它应用于

      • 一个整数或枚举类型的非易失性左值,它引用一个具有先前初始化的非易失性 const 对象,用常量表达式初始化 [注意:字符串文字 (2.14.5) 对应于一个数组的此类对象。 ——尾注],或

      • 一个非易失性的字面值类型,它引用一个用constexpr定义的非易失性对象,或者引用这样一个对象的子对象,或者

    [...]

    但在xs[0] 的评估中出现的根据 (4.1)(不是 4.2,它是数组到指针)的唯一真正的左值到右值转换是从结果左值到第一个元素。

    对于 OP 中的示例:

    int const xs[]{1, 2, 3};
    int as[xs[0]];  // error.
    

    此元素xs[0] 具有非易失性 const 整数类型,其初始化在其出现的常量表达式之前,并且已使用常量表达式进行初始化。


    顺便说一下,在[expr.const]/2 has been added to clarify的引用段落中添加了“注意”,表明这是合法的:

    constexpr char c = "hello"[0];
    

    请注意,字符串文字也是左值。


    如果有人(可以将其更改为)解释为什么xs[0] 不允许出现在常量表达式中,那就太好了。

    【讨论】:

    • 实现拒绝的原因是因为他们不相信这是段落的意图(意图是向后兼容 C++03,而 C++03 没有允许const int a = 0; int x[a];)。 C++03 也不允许"foo"[0],但该段落还允许对草案进行后来的“让我们继续进行非规范性编辑”更改,因为从字符串文字中读取被认为是可取的。实际上,bullet1 的实际意图似乎是“允许从字符串文字中读取,并保持向后兼容 C++03”。
    • (上述评论基于与 Richard Smith 的较早谈话,他为 clang 实施了 constexpr 评估)。
    • @JohannesSchaub-litb 我不确定我是否完全理解您的解释。据我所知,const int a = 1; int x[a]; 在 C++03 中是允许的,在 C++03 和 C++11 中都禁止使用零大小的数组。此外,放宽对 const 变量的约束以出现在常量表达式中会破坏向后兼容性吗?或者这是否意味着意图不为const 变量更改任何东西?
    • 是的,这一次是两个错误。上面的意思是 ",而 C++03 确实允许 const int a = 1; int x[a];"。在 C++11 中不允许这样做会破坏向后兼容性。
    猜你喜欢
    • 2012-12-16
    • 2018-11-05
    相关资源
    最近更新 更多