31

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?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
stackcpp
  • 1,275
  • 7
  • 15
  • 4
    AFAIK GCC has a "variable length arrays" feature (https://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 Jul 20 '15 at 21:05
  • 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 Jul 21 '15 at 00:15
  • @ShafikYaghmour Because it is recent, I donwload from github. Another question about 5.20[expr.const], see http://stackoverflow.com/questions/31527913/is-a-glvalue-integral-constant-expression-a-constant-expression – stackcpp Jul 21 '15 at 00:23
  • 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 Jul 21 '15 at 01:50

3 Answers3

24

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;

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • this is not just in array, `static_assert(a || 1,"");` is also ok in gcc, so gcc considers `a || 1` a constant expression, but clang doesn't – stackcpp Jul 20 '15 at 21:14
  • Yes, so it should be an error when used during the static_assert, perhaps it is constant folding the expression since it can see it has to be true. – Shafik Yaghmour Jul 20 '15 at 21:21
  • I've updated my question. `a` is not a constant expression, how about `a || 1`? – stackcpp Jul 20 '15 at 21:35
  • If `a` is not a constant expression, neither will any computations that use it (i.e. `a || 1`). An expression being constant is somewhat contingent upon all of its pieces being constant. So the answer is no. – Qix - MONICA WAS MISTREATED Jul 20 '15 at 21:53
2

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.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • There used to be text that implementations could consider other expressions to be constant expressions beyond the minimum mandated by the standard, but I can't see that any more – M.M Jul 20 '15 at 22:15
  • 2
    Back in the days before SFNAIE, having a compiler regarded additional things as constant expressions would mean that it would accept as valid code which other compilers would reject, and might generate more efficient code than would compilers which did not recognize those constant expressions, but would not change the meaning of code which would be accepted by other compilers. Adding SFNAIE to the language makes it possible to express many things which weren't possible before, but makes the fact that something *isn't* constant semantically significant. – supercat Jul 23 '15 at 22:42
1

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.

Jon Trauntvein
  • 4,453
  • 6
  • 39
  • 69
  • 1
    If the evaluation of `a` has no side-effects, then the fact that the `||` (somewhat unhelpfully IMHO) converts each operand to a 0 or 1 would mean that `a || 1` would be equivalent to `1` in C. Of course, SFINAE means a similar principle probably can't apply in C++. – supercat Jul 20 '15 at 23:17
  • 1
    The standard requires `||` to be evaluated from left to right which means it is not allowed to avoid the lvalue-to-rvalue conversion here. – Shafik Yaghmour Jul 21 '15 at 02:28