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

时间:2015-07-20 21:00:22

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

N4527 5.20 [expr.const] p5

  

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

     

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

     

- 如果对象或子对象是指针类型,则它包含具有静态存储持续时间的对象的地址,超过此类对象末尾的地址(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
            ;
        }
}

a || 1常量表达式

N4527 5.20 [expr.const] p2

  

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

     

(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核心常量表达式

3 个答案:

答案 0 :(得分:23)

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

a || 1 

也不是常量表达式,虽然我们知道表达式必须求值为true,但标准要求从左到右进行评估,并且我看不到任何允许编译器跳过{{的左值到右值转换的异常1}}。

但:

a

可以在常量表达式中使用,因为它属于const int a = 1; 强调我的)的异常:

  

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

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

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

也许5.20p2允许这样做:

gcc

作为可变长度数组作为扩展,尽管它应该使用int b[a || 1]{}; 提供警告。虽然这不能解释static_assert情况,但它们可以是常量折叠它但我不认为as-if规则会允许它被视为常量表达式。

更新,可能的gcc扩展

从这个错误报告RHS of logical operators may render LHS unevaluated in constant-expression,这看起来像是一个可能的gcc扩展名:

  

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

-pedantic
     

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

并且最终评论说:

  

我认为这是一个有目的的语言扩展,可以使用开关来禁用。如果static_assert始终是严格的,那就太好了。

这似乎是一个不合规的扩展程序,在Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr?中使用类似徒劳无功的int i; static_assert( i || true, "" ); static_assert( ! ( i && false ), "" ); 标记时应触发警告。

C ++ 11 / C ++ 14引用

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

  

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

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

  •   
  • 一个非易失性glvalue,它指的是用constexpr定义的非易失性对象,或者指的是   到这种对象的不可变子对象,或

  •   
  • 文字类型的非易失性glvalue,它引用生命周期开始的非易失性对象   在评估e;

  •   

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

  

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

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

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

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

  •   

答案 1 :(得分:2)

重复你的引用:

  

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

     

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

a涉及左值到右值的转换。由于a不是const对象,这意味着a不是核心常量表达式;因此,a || 1也不是。

但是,如果你的代码是:

const int a = 1;

然后a || 1将成为核心常量表达式。

答案 2 :(得分:1)

如果编译器检查整个赋值链,那么它可以确定" a || 1"是一个不变的表达。但是,由于a是一个变量,除非编译器检查a尚未分配,否则它无法知道" a || 1"是一个不变的表达。

相关问题