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

b1s*_*sub 13 c++ language-lawyer c++17

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

int main()
{
    constexpr int a = 10;
    const int &b = a;
    constexpr int c = b; // here
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

由于标准说(8.20,第2段[expr.const]在N4700)

表达式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;
Run Code Online (Sandbox Code Playgroud)

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

Bar*_*rry 13

核心常量表达式的一个规则是我们无法评估:

一个id-expression,引用引用类型的变量或数据成员,除非引用具有先前的初始化和任何一个

  • 它是用常量表达式初始化的
  • 它的生命始于e的评价范围内;

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

常量表达式可以是一个glvalue芯常量表达式指的是一个常量表达式的结果允许的实体(如下面所定义),或一个prvalue芯常数表达式,其值满足以下约束条件:[...]

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

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

因此,a不是一个恒定的表达.并且b,作为结果,不从常量表达式初始化,并且因此不能在核心常量表达式被使用.因此c,初始化是不正确的,因为它不是一个恒定的表达式.声明a为a static constexpr int,gcc和clang都接受该程序.

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

  • `b`用glvalue初始化,**`a`作为glvalue**不是常量表达式. (2认同)
  • @AnT在那里,我不同意.比特字段要求"整数常量表达式",它不是"常量表达式"的子集,而是"整数[...]类型的表达式,隐式转换为prvalue,其中转换后的表达式是核心常量表达式".不同的要求. (2认同)