int a = 1,是|| 1一个常数表达式?

sta*_*cpp 31 c++ language-lawyer constant-expression c++17

N4527 5.20 [expr.const] p5

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

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

- 如果对象或子对象是指针类型,则它包含具有静态存储持续时间的对象的地址,超过此类对象的结尾的地址(5.7),函数的地址或空指针值.

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

void foo(){
    int a = 1;
    int b[a || 1]{};//ok in gcc 5.1.0, error in clang 3.8.0
    static_assert(a || 1,"");//ok in gcc 5.1.0, error in clang 3.8.0
    switch(1){
        case a || 1://ok in gcc 5.1.0, error in clang 3.8.0
            ;
        }
}
Run Code Online (Sandbox Code Playgroud)

a || 1一个不变的表达


N4527 5.20 [expr.const] p2

条件表达式e是核心常量表达式,除非根据抽象机器(1.9)的规则评估e将评估以下表达式之一:

(2.7) - 左值 - 右值转换(4.1),除非适用于

(2.7.1) - 整数或枚举类型的非易失性glvalue,它引用具有前面初始化的完整非易失性const对象,用常量表达式初始化,或者

(2.7.2) - 一个非易失性glvalue,引用字符串文字的子对象(2.13.5),或者

(2.7.3) - 一个非易失性glvalue,引用用constexpr定义的非易失性对象,或引用这种对象的不可变子对象,或者

(2.7.4) - 文字类型的非易失性glvalue,指的是一个非易失性对象,其生命周期始于e的评估范围内;

a || 1一个核心常量表达式

Sha*_*our 23

a不是常量表达式(见下面的标准引用),因此:

a || 1 
Run Code Online (Sandbox Code Playgroud)

也不是一个常量表达式,虽然我们知道表达式必须求值为true,但标准要求从左到右评估,我看到没有允许编译器跳过左值到右值转换的异常a.

但:

const int a = 1;
Run Code Online (Sandbox Code Playgroud)

可以在常量表达式中使用,因为它属于5.20p2(强调我的)异常:

除非适用,否则左值到右值的转换(4.1)

  • 一个非整数或枚举类型的非易失性glvalue,它引用一个完整的非易失性const对象,具有前面的初始化,用常量表达式初始化,或者
  • 一个非易失性glvalue,引用字符串文字的子对象(2.13.5),或
  • 非易失性glvalue,指的是用constexpr定义的非易失性对象,或引用此类对象的不可变子对象,或者
  • 文字类型的非易失性glvalue,指的是一个非易失性对象,其生命周期始于e的评估

此规则也是原始案例不是常量表达式的原因,因为没有例外情况适用.

也许gcc允许这样:

int b[a || 1]{};
Run Code Online (Sandbox Code Playgroud)

作为可变长度数组作为扩展,虽然它应该提供警告使用-pedantic.虽然这不能解释static_assert情况,但它们可以是常量折叠它但我不认为as-if规则会允许它被视为常量表达式.

更新,可能的gcc扩展

从这个错误报告中,逻辑运算符的RHS可能会使常量表达式中的LHS未被评估,这看起来像是一个可能的gcc扩展:

尽管在常量表达式中使用了非常量对象,但这种编译没有任何意外:

int i;
static_assert( i || true, "" );
static_assert( ! ( i && false ), "" );
Run Code Online (Sandbox Code Playgroud)

似乎假设是|| 和&&是可交换的,但短路只能在一个方向上起作用.

最后的评论说:

我认为这是一个有目的的语言扩展,可以使用开关禁用.如果static_assert始终是严格的,那将是很好的.

这似乎是一个不符合要求的扩展,在使用-pedantic类似的徒劳无益的标志时应该触发警告它是否是一个符合标准的编译器扩展,将非constexpr标准库函数视为constexpr?.

C++ 11/C++ 14引用

Section 5.205.19C++ 14和C++ 11中的部分,C++ 14标准草案的相关引用是:

除非适用,否则左值到右值的转换(4.1)

  • 整数或枚举类型的非易失性glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化[注意:字符串文字(2.14.5)对应于此类对象的数组. - 注意],或

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

  • 文字类型的非易失性glvalue,指的是一个非易失性对象,其生命周期始于e的评估范围内;

对于C++ 11标准草案是:

除非适用,否则左值到右值的转换(4.1)

  • 一个整数或枚举类型的glvalue,它引用一个带有前面初始化的非易失性const对象,用一个常量表达式初始化,或者

  • 一个文字类型的glvalue,它指的是用constexpr定义的非易失性对象,或者指的是这样一个对象的子对象,或者

  • 一个文字类型的glvalue,它引用一个生命周期尚未结束的非易失性临时对象,用一个常量表达式初始化;