为什么'constexpr const int &a = 1;'在块范围内失败了?

Why `constexpr const int &a = 1;` failed in block scope?

本文关键字:范围内 失败 const constexpr int 为什么      更新时间:2023-10-16

N4527 7.1.5[dcl.constexpr]p9

对象声明中使用的constexpr说明符将对象声明为const。这样的对象必须具有文字类型,并且必须初始化。如果由构造函数调用初始化,则该调用应为常量表达式(5.20)。否则,或者如果在引用声明中使用了constexpr说明符,则在其初始化项中出现的每个完整表达式都必须是常量表达式。

5.20 [expr.const] p5

一个常量表达式要么是一个glvalue核心常量表达式,其值指向一个实体一个常量表达式(定义如下)的允许结果,或者一个右值核心常量表达式,其Value是一个对象,对于该对象及其子对象:

—每个引用类型的非静态数据成员指向一个实体,该实体是常量表达式的允许结果,

—如果对象或子对象是指针类型,则它包含具有静态存储时间的对象的地址、该对象结束后的地址(5.7)、函数的地址或空指针值。

实体是常量表达式允许的结果,如果它是具有静态存储时间的对象,不是临时对象,或者是值满足上述约束的临时对象,或者是函数。

void foo(){
    constexpr const int &a = 1;//error
    constexpr static const int &b = 1;//ok in gcc 5.1.0 and clang 3.8.0
}

问题:为什么constexpr const int &a = 1;在块范围内失败?

这在cwg缺陷报告2005:不正确的constexpr引用初始化需求中包含,该需求说(强调我的):

考虑如下示例:

  constexpr int f() { return 5; } // function must be constexpr
  constexpr int && q = f();       // but result is not constant
  constexpr int const & r = 2;    // temporary is still not constant
  int main() {
    q = 11;                       // OK
    const_cast< int & >( r ) = 3; // OK (temporary object is not ROMable)
    constexpr int && z = 7;       // Error? Temporary does not have static storage duration?
  }

constexpr引用必须由常量表达式初始化(7.1.5 [dcl。[Constexpr]第9段),但它可以指可修改的临时对象。这样的临时保证是静态的初始化,但不是ROMable。

用左值表达式初始化的非const constexpr引用是有用的,因为它表明引用可能是静态初始化的,或者没有底层存储

当初始化式是临时类型时,查找其地址很简单。没有理由在其计算过程中声明任何意图地址。另一方面,提供了一个初始值,即也需要是一个常量表达式,尽管它从未被处理过作为常量

本地constexpr引用的情况更糟。初始化器在执行声明时生成临时对象。临时是局部作用域的唯一对象。这将呈现constexpr没有意义,因为虽然地址计算是微不足道的,它仍然必须动态完成。

c++ 11 constexpr引用需要按引用初始化常量表达式,它必须"用static来指定对象"函数的存储时间" (c++ 11 5.20 [expr。[参]第3段。由引用授予的具有自动存储期限的临时对象

c++ 14删除了引用常量表达式和静态存储需求,用明显定义好的程序呈现失败的constexpr说明符。(GCC和Clang目前提供c++ 11诊断。)

建议解析:临时绑定到constexpr引用应该本身是constexpr,暗示const限定类型。禁止将constexpr引用绑定到临时对象,除非两者都具有static存储时间。(在局部范围内,静态说明符修复问题好。)

回应是这已经被5.20第4段禁止了:

这个问题已经在5.20 [expr]中提到了。第4段,其中包括分析中的转换和临时。