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
是核心常量表达式?
答案 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"是一个不变的表达。