The following C++11 program:
int x = 42;
void f()
{
int y = 43;
static_assert(&x < &y, "foo");
}
int main()
{
f();
}
Doesn't compile with gcc 4.7 as it complains:
error: ‘&y’ is not a constant expression
This would agree with my intuition. The address of y
potentially changes with each invocation of f
, so of course it cannot be calculated during translation.
However none of the bullet points in 5.19 [expr.const] seem to preclude it from being a constant expression.
The only two contenders I see are:
an lvalue-to-rvalue conversion...
but unless I am mistaken (?) there are no lvalue-to-rvalue conversions in the program.
And
an
id-expression
that refers to a variable [snip] unless:
- it is initialized with a constant expression
which y
is - it is initialized with the constant expression 43
.
So is this an error in the standard, or am I missing something?
Update:
It's confusing as hell, but I think I am on top of it, so let me show an example that will show off what is going on:
int x = 42;
void f()
{
int y = 43;
// address constant expressions:
constexpr int* px = &x; // OK
constexpr int* py = &y; // ERROR: pointer context for local variable
// boolean constant expressions:
constexpr bool bx = &x; // OK
constexpr bool by = &y; // OK
// comparison constant expressions:
constexpr bool eq = (&x == &y); // OK
constexpr bool lt = (&x < &y); // ERROR: undefined behaviour disqualifies
a constant expression
}
int main()
{
f();
}
First distinguish between a core constant expression (5.19p2) and a constant expression (5.19p4). Specifcally sub-expressions of a constant expression only have to be core constant expressions, not constant expressions. That is, being a constant expression is a property of the full expression, not sub-expressions. It further requires to look at the context it which the full expression is used.
So, as it turns out the gcc error is misleading. Firstly &y
may be a constant expression in some contexts. Secondly, the reason &x < &y
isn't a constant expression is because of the comparison of unrelated pointers, not of the sub-expression &y
.
A constant expression gets evaluated at compile time, not run time, and can be used in any place that a constant can be used. The constant expression must evaluate to a constant that is in the range of representable values for that type.
A constant expression is an expression that contains only constants. A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur.
A constexpr variable must be initialized at compile time. All constexpr variables are const . A variable can be declared with constexpr , when it has a literal type and is initialized. If the initialization is performed by a constructor, the constructor must be declared as constexpr .
A constant value is one that doesn't change. C++ provides two keywords to enable you to express the intent that an object is not intended to be modified, and to enforce that intent. C++ requires constant expressions — expressions that evaluate to a constant — for declarations of: Array bounds.
Let's try to determine which requirements the expression in the static_assert-declaration has to fulfil step-by-step, using n3485.
[dcl.dcl]/1
static_assert-declaration:
static_assert (
constant-expression,
string-literal) ;
[dcl.dcl]/4
In a static_assert-declaration the constant-expression shall be a constant expression that can be contextually converted to
bool
.
[expr.const]/4
Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions.
So what type of constant expression is &x < &y
? It is not an address constant expression:
[expr.const]/4
An address constant expression is a prvalue core constant expression (after conversions as required by the context) of type
std::nullptr_t
or of pointer type [...].
The type of &x < &y
is bool
as per [expr.rel]/1.
It isn't a reference constant expression either, so it must be a literal constant expression, if any.
A literal constant expression is a prvalue core constant expression of literal type [...]
Therefore, &x < &y
has to fulfil the requirements of a core constant expression.
As pointed out by TemplateRex and hvd in the comments, in this particular case, &x < &y
does not fulfil the requirements of a core constant expression:
[expr.const]/2
[a core constant expression must not contain] a relational or equality operator where the result is unspecified;
[expr.rel]/2
If two pointers
p
andq
of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results ofp<q
,p>q
,p<=q
, andp>=q
are unspecified.
However, for an example like
int arr[2] = {1, 2};
static_assert(&a[0] < &a[1], "");
The expression a < a+1
fulfils this requirement as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With