objects that're usable constant expressions [duplicate] - c++

This question already has answers here:
The question about a glvalue constant expression used in a context that requires a constant expression
(2 answers)
The value of a const variable is or is not usable in a constant expression, depending on the variable type
(2 answers)
Initialization of a constexpr variable
(2 answers)
Closed 5 months ago.
I have the following code that demonstrate my problem:
int main(void)
{
const int ci = 42;
constexpr int j = ci;
}
The above program compiles fines. But I'm expecting it to be ill-formed.
First, the initializer 42 is an integral constant expression converted to int via identity conversion; then, the converted expression is a core constant expression: ([const.expr]/9)
An integral constant expression is an expression of integral or
unscoped enumeration type, implicitly converted to a prvalue, where
the converted expression is a core constant expression.
Second, I claim that the expression E = ci is not usable in constant expression because it's not constant-initialized (i.e, it's variable has no static duration) even though the expression ci is potentially-constant variable (i.e, it's variable of const-qualified integral type). So you can't apply the rule [expr.const]/4 because it requires the object to be potentially-constant as well as constant-initialized:
A constant-initialized potentially-constant variable V is usable in constant expressions at a point P if V's initializing declaration D
is reachable from P and ..
So per my understanding, for variable ci to be constant-initialized, it has to have a static duration as well as a constant-expression initializer.
Assuming my understanding is correct so far, I will continue.
In the initialization of j, an lvalue-to-rvalue conversion is performed on the glvalue ci; but this conversion is applied to a non-volatile glvalue that refers to an object ci that is not usable in constant expressions. So I'm expecting the program to be ill-formed because the expression E evaluates an lvalue-to-rvalue conversion and neither rule in [expr.const]/(5.9) permits it. Am I correct? What I'm missing/conflating here? Am I missing any wording?
As a sidenote, in C++17, the rule regarding this point is more restricted and clear at least for me:
an lvalue-to-rvalue conversion 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 [..]
It's definitely clear to me that the wording is applied here and (2.7.1) is satisfied. But the wording regarding this point specifically is changed in C++20: The term usable "usable in constant expressions" appears since C++20 per my search. Note that, if possible, I need a C++20 answer.

I claim that the expression E = ci is not usable in constant expression because it's not constant-initialized (i.e, it's variable has no static duration)
constant-initialized is defined in [expr.const]/2. It is not the same as "has constant initialization", and does not care about storage duration.

By the definition of "constant-initialized" ([expr.const]/2), the following is constant-initialized because 42 is a constant expression.
int ci = 42;
By the definition of "potentially-constant" ([expr.const]/3), the following is potentially-constant because the variable is a const-qualified integral type.
const int ci;
If you have both of these at function scope, then you have a variable that is usable in constant expressions from the point of definition until the end of the variable's scope (special case of [expr.const]/4).

The expression ci is constant expression. There's no need to request [expr.const]/5 to know whether the expression is core constant expression.

Related

How to make `this` pointer constant expression?

This is a follow-up question is my previous question: Why are member functions returning non-static data members not core constant expressions?
The reduced version of the example mentioned in that question is:
struct S {
const bool x = true;
constexpr bool f() { return x; }
};
int main() {
S s{};
static_assert(s.f()); // error: 's' is not a constexpr;
}
The applicable wording from the standard is N4861: [expr.const]/(5.1):
An expression E is a core constant expression unless the evaluation
of E, following the rules of the abstract machine
([intro.execution]), would evaluate one of the following:
(5.1) this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of E;
As far as I can parse, the expression E is s.f() and it evaluates this since s.f() returns a non-static member this->x. But that falls under the "except" part: the member function s.S::f() is constexpr function that's being evaluated as part of s.f(). If I parsed correctly, I'm expecting s.f() to be constant expression and the assertion success.
However, this bullet doesn't specify a requirement that says that s has to be a constant expression. I can't understand why declaring s as constexpr compiles the program even though there's no requirement, defined in this bullet, for s to be constexpr.
I'm just applying the wording (5.1) in my example but I can't see that constexpr is required here unless I'm missing any other rule.
Because return x; performs lvalue-to-rvalue conversion, the whole kaboodle is not a core constant expression:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue that refers to an object that is usable in constant expressions, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
lvalue-to-rvalue conversion is applied to this->S::x, which is generally forbidden, and neither of the exceptions apply to permit it.
The more relevant exception applies if x (which resolves to this->S::x) is an object that is usable in constant expressions. But it only would be if the struct S object were usable in constant expressions:
a non-mutable subobject or reference member of any of the above.
That requires it to be potentially-constant:
A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type.
A constant-initialized potentially-constant variable is usable in constant expressions at a point P if ...
And S s{}; is not potentially-constant. So it is not usable in constant expressions, and neither are its subobjects.
To answer the title question, this is not a core constant expression, because it is the address of an object with automatic storage duration; that address may change at runtime. This is completely irrelevant for the static_assert in the question code: Being a constant pointer value is neither necessary nor sufficient for a this pointer to be "usable in constant expressions", which in turn is not sufficient for the object found through the pointer to be usable in constant expressions.

Why this expression is not constant expression [duplicate]

This is a follow-up question is my previous question: Why are member functions returning non-static data members not core constant expressions?
The reduced version of the example mentioned in that question is:
struct S {
const bool x = true;
constexpr bool f() { return x; }
};
int main() {
S s{};
static_assert(s.f()); // error: 's' is not a constexpr;
}
The applicable wording from the standard is N4861: [expr.const]/(5.1):
An expression E is a core constant expression unless the evaluation
of E, following the rules of the abstract machine
([intro.execution]), would evaluate one of the following:
(5.1) this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of E;
As far as I can parse, the expression E is s.f() and it evaluates this since s.f() returns a non-static member this->x. But that falls under the "except" part: the member function s.S::f() is constexpr function that's being evaluated as part of s.f(). If I parsed correctly, I'm expecting s.f() to be constant expression and the assertion success.
However, this bullet doesn't specify a requirement that says that s has to be a constant expression. I can't understand why declaring s as constexpr compiles the program even though there's no requirement, defined in this bullet, for s to be constexpr.
I'm just applying the wording (5.1) in my example but I can't see that constexpr is required here unless I'm missing any other rule.
Because return x; performs lvalue-to-rvalue conversion, the whole kaboodle is not a core constant expression:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue that refers to an object that is usable in constant expressions, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
lvalue-to-rvalue conversion is applied to this->S::x, which is generally forbidden, and neither of the exceptions apply to permit it.
The more relevant exception applies if x (which resolves to this->S::x) is an object that is usable in constant expressions. But it only would be if the struct S object were usable in constant expressions:
a non-mutable subobject or reference member of any of the above.
That requires it to be potentially-constant:
A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type.
A constant-initialized potentially-constant variable is usable in constant expressions at a point P if ...
And S s{}; is not potentially-constant. So it is not usable in constant expressions, and neither are its subobjects.
To answer the title question, this is not a core constant expression, because it is the address of an object with automatic storage duration; that address may change at runtime. This is completely irrelevant for the static_assert in the question code: Being a constant pointer value is neither necessary nor sufficient for a this pointer to be "usable in constant expressions", which in turn is not sufficient for the object found through the pointer to be usable in constant expressions.

Why is this expression not a constant expression?

The expression b in this code shall be a core constant expression
int main()
{
constexpr int a = 10;
const int &b = a;
constexpr int c = b; // Here
return 0;
}
since the standard says (8.20, paragraph 2 [expr.const] in n4700)
An expression e is a core constant expression unless the evaluation of
e would evaluate one of the following expressions:
...
an lvalue-to-rvalue conversion (7.1) unless it is applied to
...
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
First, the expression b in the above code is an lvalue (which is also a glvalue) since it's a reference, thereby being a variable (8.1.4.1, paragraph 1
[expr.prim.id.unqual]):
The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if the identifier designates a bit-field (11.5).
Second, the object the variable b denotes is a, and it's declared with constexpr. However, GCC complains:
./hello.cpp: In function ‘int main()’:
./hello.cpp:6:20: error: the value of ‘b’ is not usable in a constant expression
constexpr int c = b;
^
./hello.cpp:5:13: note: ‘b’ was not declared ‘constexpr’
const int &b = a;
As far as I can tell, a reference is not an object, so the above bullet apparently suggests that a shall be declared with constexpr. Am I missing something?
The reason why I don't agree with GCC is that GCC sees b as an object, thereby requiring it to be declared with constexpr. However, b is not an object!
One of the rules for core constant expressions is that we can't evaluate:
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
b is an id-expression that refers to a variable of reference type with preceding initialization. However, it is initialized from a. Is a a constant expression? From [expr.const]/6:
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints: [... ]
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.
a is a glvalue core constant expression (it doesn't hit any of the restrictions in expr.const/2), however it is not an object with static storage duration. Nor is it a function.
Hence, a is not a constant expression. And b, as a result, isn't initialized from a constant expression and so can't be used in a core constant expression. And thus c's initialization is ill-formed as not being a constant expression. Declare a as a static constexpr int, and both GCC and Clang accept the program.
C++, you magical beast.
All quotes given in this answer are from the current working draft.
Given your example:
int main()
{
constexpr int a = 10;
const int &b = a;
constexpr int c = b; // here
return 0;
}
First of all, let's take your example line by line. The first line is a definition of a constexpr variable named by identifier a:
constexpr int a = 10;
The initializer 10 is an integral constant expression, hence it's a core constant expression, per [expr.const]/9:
An integral constant expression is an expression of integral or
unscoped enumeration type, implicitly converted to a prvalue, where
the converted expression is a core constant expression. [ Note: Such
expressions may be used as bit-field lengths, as enumerator
initializers if the underlying type is not fixed, and as alignments.
 — end note ]
And it's a constant expression because it's a prvalue core constant expression that satisfies the [expr.const]/12 constraints:
A constant expression is [..] a prvalue core constant expression
whose value satisfies the following constraints:
(12.1) if the value is an object of class type, each non-static data
member of reference type refers to an entity that is a permitted
result of a constant expression,
(12.2) if the value is of pointer
type, it contains the address of an object with static storage
duration, the address past the end of such an object ([expr.add]), the
address of a non-immediate function, or a null pointer value
(12.3)
if the value is of pointer-to-member-function type, it does not
designate an immediate function, and
(12.4) if the value is an object
of class or array type, each subobject satisfies these constraints for
the value.
Hence, the initializer expression 10 is a prvalue core constant expression that's neither a pointer type nor a class type nor an array type. Hence, it's a constant expression.
The second line declares/defines a reference to a named by identifier b:
const int &b = a;
In order to know whether the initializer expression a is a core constant expression, you have to invoke [expr.const]/5:
An expression E is a core constant expression unless the evaluation
of E, following the rules of the abstract machine ([intro.execution]),
would evaluate one of the following: [..]
At this point, the evaluation of E does not evaluate any of the constraints defined in [expr.const]/5. Therefore, the expression E is a core constant expression.
But being E is a glvalue core constant expression that doesn't mean that E is also constant expression; so you have to check [expr.const]/12:
A constant expression is either a glvalue core constant expression
that refers to an entity that is a permitted result of a constant
expression [..]
An entity is a permitted result of a constant expression if it is an
object with static storage duration that either is not a temporary
object or is a temporary object whose value satisfies the above
constraints, or if it is a non-immediate function.
So the entity a is not a permitted result of constant expression because that entity neither refers to an object with static storage duration nor a temporary object or non-immediate function. Therefore, the glvalue core constant expression a is not a constant expression.
The third line defines an constexpr variable named by identifier c initialized by the variable b:
constexpr int c = b;
Again you have to invoke [expr.const]/5 in order to know whether the expression b is a core constant expression or not. As you already noticed, the bullet (5.8) maybe satisfied since the initialization of c involves an lvalue-to-rvalue conversion. So per [expr.const]/5
An expression E is a core constant expression unless the evaluation of
E [..] would evaluate one of the following:
(5.8) an lvalue-to-rvalue conversion unless it is applied to
(5.8.1) a non-volatile glvalue that refers to an object that is usable in constant expressions, or
(5.8.2) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
Note that the document to which I refer doesn't have the following bullet. But C++20 documents have it. And I don't know why it's removed from the current working draft. ([expr.const]/(5.12)):
(5.12) an id-expression that refers to a variable or data member of
reference type unless the reference has a preceding initialization and
either
(5.12.1) it is usable in constant expressions or
(5.12.2) its
lifetime began within the evaluation of E;
Even if it's checked, the expression E is still not a core constant expression because the reference is not usable in constant expressions (see below) and its lifetime doesn't begin within the evaluation of E.
Back to our explanation. The bullet (5.8) is satisfied since the expression E evaluates an lvalue-to-rvalue conversion. So (5.8.1) is tried first.
Indeed, the lvalue-to-rvalue conversion is applied to a non-volatile glvalue (b). But, Is this glvalue refers to an object that's usable in constant expressions? First, you have to check whether it's a constant-initialized variable, then also you have to check whether it's a potentially-constant variable, so [expr.const]/2 is checked first:
A variable or temporary object o is constant-initialized if
(2.1) either it has an initializer or its default-initialization results in some initialization being performed, and
(2.2) the full-expression of its initialization is a constant expression when interpreted as a constant-expression [..]
The first bullet (2.1) is satisfied because the variable b has an initializer. But the second bullet isn't satisfied because the full-expression of its initialization is not a constant expression because its initializer a is not a constant expression as aforementioned. Therefore, the variable b is not constant-initialized.
Just for completeness, the [expr.const]/3 is tried:
A variable is potentially-constant if it is constexpr or it has a
reference or const-qualified integral or enumeration type.
Since the variable b has a reference type, it's considered to be potentially-constant. Note, there's no need to check whether the variable b is potentially-constant because intuitively you can't even enter the [expr.const]/4 since it requires the variable to be constant-initialized:
A constant-initialized potentially-constant variable is usable in
constant expressions at a point P if its initializing declaration D is
reachable from P [..]
(emphasis mine)
Even though the variable is potentially-constant, it's not usable in a constant expression because it's not constant-initialized. So we ended up with that the variable b is not usable in constant expressions (as the GCC diagnostic hints to you). Therefore, the bullet (5.8.1) is not satisfied: Hence, the expression E is not a core constant expression and therefore not a constant expression.

Is a glvalue integral constant expression a constant expression?

N4527 5.20 [expr.const]p3
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression.
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:
(5.1) — each non-static data member of reference type refers to an entity that is a permitted result of a constant
expression, and
(5.2) — 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(){
const int a = 1;//a has automatic storage duration
// all ok in gcc 5.1.0 and clang 3.8.0
int b[a]{};
static_assert(a,"");
switch(1){
case a:
;
}
}
Question1: Is a an integral constant expression?
Question2: Is a a constant expression?
Question3: Is a glvalue integral constant expression a constant expression?
Question4:
If the answer of question 3 is yes,
does this conflict with 5.20 p3 if the object has automatic storage duration?
Is a an integral constant expression?
In the following contexts:
int b[a]{};
static_assert(a,"");
switch(1){
case a:
;
}
yes, a is an integral constant expression. Starting with your first quote:
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted
to a prvalue, where the converted expression is a core constant expression.
'a' is an integral type, in your cases it will be implicitly converted to a prvalue, so now is a a core constant expression? Yes, if we go back to paragraph 2 which defines what is not a core constant expression:
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
it has the following clause:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
with the following exception:
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
which applies to a since it is non-volatile, const and is initialized with a constant expression.
Is a a constant expression?
In the same contexts as above, yes, since we can see from the quote above it is a core constant expression.
Is a glvalue integral constant expression a constant expression?
No, in order for it to be a integral constant expression it must be converted to a prvalue and threfore can not be a glvalue.

Is taking the address of a local variable a constant expression in C++11?

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.
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 and q 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 of p<q, p>q, p<=q, and p>=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.
Yes, you're missing the fact that, while y itself is initialised with a constant expression, that's not the same as &y.
The address of y can vary a great deal depending on your call stack history.
Paragraph 3 of C++11 5.19 Constant expressions states the conditions under which the address-of operator can be considered a constant expression (how core constant expressions detailed in paragraph 2 are allowed to morph into "real" constant expressions):
... An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t. Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions.
Since &y is none of those things, it's not considered a constant expression.
Taking the address of something is not the culprit here, but rather the pointer comparison using operator< on unrelated objects.
Relational operators on pointers is only specified for pointers to objects within the same class or within an array (5.9 Relational operators [expr.rel], points 3 and 4.) Relational comparison for pointers to unrelated objects is unspecified.
Comparing the address for equality rather than ordering does work:
int x = 42;
void f()
{
int y = 43;
static_assert(&x != &y, "foo");
^^ <--- "<" on unrelated objects is unspecified
}
int main()
{
f();
}
Live example
Just to show that this has nothing to do with const-expressions per se,
void f()
{
int y[2] = { 42, 43 };
static_assert(&y[0] < &y[1], "foo");
^ <--- "<" on objects within an array is specified
}
int main()
{
f();
}
Another live example.
5.19p2 does not define constant expressions, it defines core constant expressions.
A core constant expression only becomes a constant expression if it conforms to one of the rules in 5.19p3. There, the relevant part was already pointed out by jrok:
An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t.
Your core constant expression &y does not evaluate to any of those, so it's not an address constant expression, and thus not a constant expression.
Sorry, I agree that the previous answer was probably an incorrect reading of the items. Instead, the actual relevant clause is 5.19 [expr.const] paragraph 3 which reads (highlightening added):
A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An
integral constant expression is a literal constant expression of integral or unscoped enumeration type. [ Note:
Such expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field lengths (9.6), as enumerator initializers
if the underlying type is not fixed (7.2), as null pointer constants (4.10), and as alignments (7.6.2). —end
note ] A converted constant expression of type T is a literal constant expression, implicitly converted to type T,
where the implicit conversion (if any) is permitted in a literal constant expression and the implicit conversion
sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5),
and integral conversions (4.7) other than narrowing conversions (8.5.4). [ Note: such expressions may be used
as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), and as integral or
enumeration non-type template arguments (14.3). —end note ] A reference constant expression is an lvalue
core constant expression that designates an object with static storage duration or a function. An address
constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an
object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core
constant expression of type std::nullptr_t. Collectively, literal constant expressions, reference constant
expressions, and address constant expressions are called constant expressions.
A core constant expression isn't directly a constant expression, there are additional conditions which are spelled out in this third paragraph.
In C++ pre-C++11:
Other expressions [than integral constant expressions] are
considered constantexpressions only for the purpose of
nonlocal static object initialization (3.6.2). Such
constant expressions shall evaluate to one of the following:
[...]
-- an address constant expression,
[...]
An address constant expression is a pointer to an lvalue
designating an object of static storage duration, a string
literal (2.13.4), or a function.
Since y doesn't have static storage duration, &y would not
be a constant expression.
C++11 seems to have changed this; I suspect that this is an
oversignt, however. (C++ pre-C++11 lists the things that are
constant expressions. C++11 lists the things that aren't. It
would be easy for one to have been forgotten.)
Regardless, of course: your comparison isn't usable in standard
C++; the results of comparing two addresses which don't point
into the same object is unspecified. (On the other hand, I have
occasionally used something similar in machine dependent code.
Not statically, but on platforms like Linux on PC or Solaris,
it's possible to determine whether a pointer points to an object
with static lifetime, and auto variable, or dynamically
allocated memory with such tricks.)
EDIT:
The answser by paxdiablo has quoted the passage I didn't find in
my reading of C++11; C++11 follows the same rule as C++ pre-11
in this respect, and in order to be a constant address
expression, the address must be that of an object with static
lifetime (or a function, or a null pointer).