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

在...创建的问题 Thu, Jul 23, 2015 12:00 AM

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

    
31
  1. AFAIK GCC具有“可变长度数组”功能( gcc.gnu.org/onlinedocs/gcc/Variable-Length.html )可能负责int b[a || 1]工作。我不知道如何正确回答你的问题。
    2015-07-20 21:05:38Z
  2. 引用N4527的任何特殊原因?对于C ++ 11和C ++ 14,这个特殊情况应该是相同的,尽管引号会略有不同。
    2015-07-21 00:15:30Z
  3. @ ShafikYaghmour因为它是最近的,所以我从github下载。关于5.20 [expr.const]的另一个问题,参见 stackoverflow.com/questions/31527913 /...
    2015-07-21 00:23:15Z
  4. 这个问题是C ++ 1z是一个移动目标,因此措辞可能会发生变化。然而,对于C ++ 11/14,它们已经完成,并且据我所知只能通过缺陷报告应用更改,这些报告虽然不完美,但有点自我记录。 C ++ 11/14也是人们在制作中使用的东西(可能主要是C ++ 11 ),因此更具相关性,恕我直言。对于这个问题,据我所知,其他答案也是一样的,尽管引号将特定于具体标准。
    2015-07-21 01:50:54Z
  5. 醇>
    3答案                              3 跨度>                         

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

     
    a || 1 
    

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

    但:

     
    const int a = 1;
    

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

      

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

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

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

    也许gcc允许这样做:

     
    int b[a || 1]{};
    

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

    更新,可能的gcc扩展

    从这个错误报告逻辑运算符的RHS可能会使LHS在不变的情况下被评估为表达式这看起来像是一个可能的gcc扩展名:

      

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

     
    int i;
    static_assert( i || true, "" );
    static_assert( ! ( i && false ), "" );
    
         

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

    并且最终评论说:

      

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

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

    C ++ 11 /C ++ 14引用

    5.20节是C ++ 14和C ++ 11中的5.19节,C ++ 14标准草案的相关引用是:

      

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

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

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

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

    •   

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

      

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

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

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

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

    •   
        
    23
    2017-05-23 10:29:33Z
    1. 这不只是在数组中,static_assert(a || 1,"");在gcc中也可以,所以gcc认为a || 1是一个常量表达式,但是clang不是
      2015-07-20 21:14:40Z
    2. 是的,所以在static_assert期间使用它应该是一个错误,也许它是常量折叠表达式,因为它可以看到它必须是真的。
      2015-07-20 21:21:14Z
    3. 我已经更新了我的问题。 a不是常量表达式,a || 1怎么样?
      2015-07-20 21:35:59Z
    4. 如果a不是常量表达式,则任何使用它的计算都不会(即a || 1)。表达式保持不变在某种程度上取决于它的所有部分是否恒定。所以答案是否定的。
      2015-07-20 21:53:13Z
    5. 醇>

    重复你的引用:

      

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

         

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

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

    但是,如果你的代码是:

     
    const int a = 1;
    

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

        
    2
    2015-07-20 21:59:10Z
    1. 曾经有一些文本,实现可以认为其他表达式是超出标准规定的最小值的常量表达式,但我不能再看到
      2015-07-20 22:15:39Z
    2. 回到SFNAIE之前的几天,让编译器将其他东西视为常量表达式意味着它会接受其他编译器拒绝的有效代码,并可能产生更多有效的代码比不识别那些常量表达式的编译器有效,但不会改变其他编译器可以接受的代码含义。将SFNAIE添加到语言中可以表达许多以前不可能的事情,但是事实上不是在语义上具有重要意义。
      2015-07-23 22:42:38Z
    3. 醇>

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

        
    1
    2015-07-20 21:05:41Z
    1. 如果a的评估没有副作用,那么||(有点无益的恕我直言)将每个操作数转换为0或1这一事实意味着a || 1会相当于C中的1.当然,SFINAE意味着类似的原则可能不适用于C ++。
      2015-07-20 23:17:08Z
    2. 标准要求从左到右评估||,这意味着不允许在此处避免左值到右值的转换。
      2015-07-21 02:28:15Z
    3. 醇>
来源放置 这里