31
votes

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
            ;
        }
}

Is a || 1 a constant expression?


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;

Is a || 1 a core constant expression?

3
AFAIK GCC has a "variable length arrays" feature (gcc.gnu.org/onlinedocs/gcc/Variable-Length.html) which might be responsible for int b[a || 1] working. I don't know enough to answer your question properly though.liori
Any particular reason to quote N4527? This particular case should be the same for C++11 and C++14 although the quotes would be slightly different.Shafik Yaghmour
@ShafikYaghmour Because it is recent, I donwload from github. Another question about 5.20[expr.const], see stackoverflow.com/questions/31527913/…stackcpp
This issue is that C++1z is a moving target, so it is possible the wording could shift. Whereas with C++11/14 they are done and changes could as far as I know only be applied via defect reports, which are somewhat self-documenting, although not perfect. C++11/14 is also what people are using in production(probably mostly C++11) and so is more relevant, IMHO. For this question and as far as I know the others as well the answer would be the same although the quotes will be specific to the specific standard.Shafik Yaghmour

3 Answers

24
votes

a is not constant expression(see standard quote below) and therefore:

a || 1 

Is not a constant expression either, although we know the expression has to evaluate to true the standard requires left to right evaluation here and I see no exceptions that would allow the compiler to skip the lvalue-to-rvalue conversion of a.

but:

const int a = 1;

Could be used in a constant expression because it fall under the exception from 5.20p2 (emphasis mine):

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

This rule is also why the original case is not a constant expression since none of the exception apply.

Perhaps gcc is allowing this:

int b[a || 1]{};

as a variable length array as an extension, although it should provide a warning using -pedantic. Although that would not explain the static_assert case, they could be constant folding it but I don't think the as-if rule would allow it to be considered a constant expression.

Update, possible gcc extension

From this bug report RHS of logical operators may render LHS unevaluated in constant-expression this looks like a possible gcc extension:

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.

and the final comment says:

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.

This seems like a non-conforming extension that should trigger a warning when using the -pedantic flag similar in vain to issue in Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr?.

C++11/C++14 Quote

Section 5.20 is section 5.19 in C++14 and C++11, the relevant quote from the draft C++14 standard is:

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;

and for the draft C++11 standard is:

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
votes

Repeating your quote:

(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 involves an lvalue-to-rvalue conversion. Since a is not a const object, that means a is not a core constant expression; therefore a || 1 is not one either.

However if your code were:

const int a = 1;

then a || 1 would be a core constant expression.

1
votes

If the compiler checks the entire assignment chain, then it can determine that "a || 1" is a constant expression. However, since a is a variable, unless the compiler checks that a has not been assigned, it has no way of knowing that "a || 1" is a constant expression.