问题
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?
回答1:
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:
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.
回答3:
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.
来源:https://stackoverflow.com/questions/31526278/int-a-1-is-a-1-a-constant-expression