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

int a=1, is a || 1 a constant expression?

N4527 5.20[expr.const]p5

A constant expression is either a glvalue core constant expression whose value refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

— each non-static data member of reference type refers to an entity that is a permitted result of a constant expression, and

— if the object or subobject is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

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

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

(2.7) — an lvalue-to-rvalue conversion (4.1) unless it is applied to

(2.7.1) — a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

(2.7.2) — a non-volatile glvalue that refers to a subobject of a string literal (2.13.5), or

(2.7.3) — a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or

(2.7.4) — a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

a || 1核心常量表达式吗?

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

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

a || 1 

也不是常量表达式,虽然我们知道表达式必须计算为真,但标准在这里要求从左到右计算,而且我看不到允许编译器跳过左值到右值转换的例外情况a.

但是:

const int a = 1;

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

an lvalue-to-rvalue conversion (4.1) unless it is applied to

  • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
  • a non-volatile glvalue that refers to a subobject of a string literal (2.13.5), or
  • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or
  • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e

这个规则也是为什么原始大小写不是常量表达式的原因,因为 none 例外适用。

也许 gcc 允许这样做:

int b[a || 1]{};

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

更新,可能的 gcc 扩展

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

This compiles without incident, despite using a non-constant object in a constant-expression:

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

It appears to be assuming that || and && are commutative, but short-circuiting only works in one direction.

最后的评论是:

I think this is a purposeful language extension, which could use a switch to disable. It would be nice if static_assert were always strict.

这似乎是一个不符合规范的扩展,在使用 -pedantic 标志时应该触发警告,类似于 Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr?.

中的问题

C++11/C++14 引用

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

an lvalue-to-rvalue conversion (4.1) unless it is applied to

  • a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [ Note: a string literal (2.14.5) corresponds to an array of such objects. —end note ], or

  • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or

  • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

C++11 标准草案是:

an lvalue-to-rvalue conversion (4.1) unless it is applied to

  • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or

  • a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or

  • a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;

重复你的话:

(2.7) — an lvalue-to-rvalue conversion (4.1) unless it is applied to

(2.7.1) — a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

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

但是,如果您的代码是:

const int a = 1;

那么a || 1就是一个核心常量表达式。