为什么这个表达式不是一个常量表达式?

时间:2017-12-20 15:01:12

标签: c++ language-lawyer c++17

此代码中的表达式b应为核心常量表达式

int main()
{
    constexpr int a = 10;
    const int &b = a;
    constexpr int c = b; // here
    return 0;
}

因为标准说(n4700中的8.20,第2段[expr.const])

  

表达式e核心常量表达式,除非评估   e将评估以下表达式之一:

     
      
  • ...

  •   
  • 左值 - 右值转换(7.1),除非它适用于

         
        
    • ...

    •   
    • 非易失性glvalue ,指使用constexpr 定义的非易失性对象,或引用此类对象的不可变子对象,或

    •   
  •   

首先,上面代码中的表达式b是一个左值(也是一个glvalue),因为它是一个引用,因此是一个变量(8.1.4.1,第1段) [expr.prim.id.unqual]):

  

如果实体是函数,则表达式是左值,   变量,或者数据成员和prvalue;如果标识符指定位字段(11.5),则它是位字段。

其次,变量b表示的对象是a,它是用constexpr声明的。但是,gcc抱怨

./hello.cpp: In function ‘int main()’:
./hello.cpp:6:20: error: the value of ‘b’ is not usable in a constant expression
  constexpr int c = b;
                    ^
./hello.cpp:5:13: note: ‘b’ was not declared ‘constexpr’
  const int &b = a;

据我所知,引用不是对象,所以上面的子弹显然表明a应该用constexpr声明。我错过了什么吗?我不同意gcc的原因是gcc将b视为对象,因此需要使用constexpr声明它。但是,b不是对象!

1 个答案:

答案 0 :(得分:13)

核心常量表达式的一个规则是我们can't evaluate

  

id-expression 引用引用类型的变量或数据成员,除非引用具有先前的初始化并且

     
      
  • 使用常量表达式
  • 初始化   
  • 其生命周期始于e;
  • 的评估范围内   

b id-expression ,它引用具有先前初始化的引用类型的变量。但是,它是从a初始化的。 a是一个常量表达式吗?来自[expr.const]/6

  

常量表达式是一个glvalue核心常量表达式,它引用一个实体,它是一个常量表达式的允许结果(如下定义),或一个prvalue核心常量表达式,其值满足以下约束:[...]

     

实体是允许的常量表达式结果如果它是具有静态存储持续时间的对象,该对象不是临时对象,或者是其值满足上述约束的临时对象,或者它是一个功能。

a是glvalue核心常量表达式(它没有达到expr.const / 2中的任何限制),但它具有静态存储持续时间的对象。它也不是一种功能。

因此,a不是常量表达式。因此,b不是从常量表达式初始化的,因此不能在核心常量表达式中使用。因此c的初始化是不正确的,因为它不是一个常量表达式。将a声明为static constexpr int,gcc和clang都接受该程序。

C ++,你是神奇的野兽。