How to make `this` pointer constant expression? - c++

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.

Related

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.

Temporary objects that are usable in constant expressions

As a follow-up to this question, clang accepts the code provided there. This question has the following code:
constexpr int func(int const& rf){
return rf;
}
int main(){
constexpr int value = func(0);
}
This question has a good answer, but it follows the C++17 standard. As far as I can tell, the wording regarding constant expression rules is relatively changed from C++17 to C++20 and later.
Basically, It has to determine whether the call expression func(0) is a constant expression; so firstly we have to know whether the call expression is core constant expression or not, which is governed by the rules defined in [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:
(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;
The rule (5.8) is applied because the expression E evaluates an lvalue-to-rvalue conversion: that's, the lvalue rf has to be converted to prvalue as the returned value of the function call.
According to (5.8.1), the expression rf is a non-volatile glvalue; but, does it is usable in constant expressions? Per [expr.const]/4:
[..]
An object or reference is usable in constant expressions if it is
(4.4) a variable that is usable in constant expressions, or
[..]
(4.7) a temporary object of non-volatile const-qualified literal type whose lifetime is extended ([class.temporary]) to that of a
variable that is usable in constant expressions, or
I'm not so sure whether bullet (4.7) is applicable to this case. But I think that it might be applicable because note that rf is bound to a materialized temporary of non-volatile const-qualified literal type whose lifetime is extended and that temporary is usable in constant expression because it's potentially-constant as well as constant-initialized.
Also, I can't find any reason why (4.4) is not applicable: rf is a variable and it's usable in constant expressions because it's potentially-constant and constant-initialized with the value 0.
So that's my confusion: which bullet (if any) is applicable here? and why?
If neither of (4.4) or (4.7) is applicable to this case, that means rf may not be usable in constant expression which also means that (5.8.1) is not satisfied and (5.8.2) is then tried. I'm not having any problem with (5.8.2): if (5.8.1) failed, (5.8.2) succeeded because the lifetime of rf exactly began within the evaluation of func(0). If that's the case, why (5.8.1) is not satisfied? My confusion specifically is why (4.7) is not satisfied.
Also note that [expr.const]/(5.12) is not reached: (5.8) is tried first.
p5.8.1 is not satisfied. rf does not refer to "an object that is usable in constant expressions", because:
p4.4 is not satisfied: the object that rf refers to is not a variable (it is a temporary object). (Notice that whether or not rf itself is a "variable that is usable in constant expressions" is not relevant for p4.4; the test of p5.8.1 is whether the object that the glvalue refers to is a variable that is usable in constant expressions. But see p4.7, below.)
p4.5, 4.6, and 4.8 are obviously not applicable.
p4.7 is not satisfied. Leaving aside the question of whether the temporary object of type int is const-qualified or not (which has been discussed in a previous question), the reference rf is not "a variable that is usable in constant expressions" anyway, because a function parameter doesn't have an initializing declaration.
I would answer this question from the perspective of the c++20 standard since the bullet [expr.const] p5.12 is removed and is taken place by the new concept in the c++23 draft.
First, the constexpr specifier requires the full-expression of the initialization of the variable value to be a constant expression, and a constant expression is either a glvalue core constant expression or a prvalue core constant expression, which is regulated by [expr.const] p5, where the bullet [expr.const] p5.2, [expr.const] p5.8, and [expr.const] p5.12 are relevant here.
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 invocation of a non-constexpr function
[...]
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;
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 usable in constant expressions or
its lifetime began within the evaluation of E;
In your example, func is a constexpr function, hence, it does not violate the first bullet in the list. The variable rf that appears in the return statement requires lvalue-to-rvalue conversion to apply to rf, where rf is a glvalue that refers to the object that is of literal type(int), and the lifetime of the object began within the evaluation of the full-expression of the initialization, see [class.temporary] p1 and [intro.execution] p5, hence the whole full-expression of the initialization still does not violate the second bullet in the list.
For the third bullet, since the id-expression rf is of reference type, however, its lifetime still began within the evaluation of the full-expression of the initialization, see [basic.life] p1, because the initialization of rf occurs in the evaluation of the full-expression of the initialization. So, eventually, the full-expression of the initialization of the variable value is a constant expression.
For the concept preceding initialization, it basically means it has been initialized. Anyway, it is CWG2186. However, as I said in the initial, the bullet is just removed in c++23 standard.
After hard searching and breaking down the rules from the standard, I would add a C++20 answer for this question.
constexpr int func(int const& rf){
return rf;
}
int main(){
constexpr int value = func(0);
}
As said above we need to determine whether the expression func(0) is a core constant expression or not.
Per [expr.const]/5, the first bullet we encountered that need to be checked is (5.8) since an lvalue-to-rvalue is applied to rf:
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.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 a literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
The bullet (5.8.1) is tried first: The lvalue-to-rvalue is applied to a non-volatile glvalue which is rf; but does this glvalue refers to an object that's usable in constant expressions? In fact, rf refers to a temporary object. In order to know whether this temporary object is usable in constant expression, we've to request [expr.const]/4: Both p4.4 and p4.7 are needed to be checked here:
[..] An object or reference is usable in constant expressions if it is
(4.4) a variable that is usable in constant expressions, or
[..]
(4.7) a temporary object of non-volatile const-qualified literal type whose lifetime is extended ([class.temporary]) to that of a
variable that is usable in constant expressions, or [..]
Intuitively, the bullet (4.4) is not applied because rf refers to a temporary object, not to a variable.
On other hand (4.7) is relevant here: rf refers to a temporary object of non-volatile const-qualified (per [dcl.init.ref]/(5.3)) literal type (per [basic.types.general]/10), and its lifetime is extended (per [class.temporary]); but in order for (4.7) to apply completely, the lifetime of the temporary has to be extended to that of a variable usable in constant expressions: In other words, the variable (rf) that extends the lifetime of the temporary has to usable in constant expressions. So does the variable rf usable in constant expressions?
In order to know whether the variable rf is usable in constant expression, we have to know whether it's potentially-constant and constant-initialized. So per [const.expr]/3:
A variable is potentially-constant if it is constexpr or it has
reference or const-qualified integral or enumeration type.
So, the variable rf is potentially-constant because it's of reference type.
But, per [const.expr]/2:
A variable or temporary object o is constant-initialized if
(2.1) either it has an initializer [..] and
(2.2) the full-expression of its initialization is a constant expression [..]
I'm not so sure whether (2.1) is satisfied or not: That's, I can't tell whether the variable rf has an initializer or not. I will assume that the rf has an initializer which is the materialized temporary even though I made a question on that.
For (2.2) to be applied, the full-expression of the initialization of rf has to be a constant expression. But, the variable rf is bound to a temporary that's not a constant expression per [const.expr]/11:
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:
(11.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,
(11.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,
(11.3) - if the value is of pointer-to-member-function type, it does not designate an immediate function, and
(11.4) - if the value is an object of class or array type, each subobject satisfies these constraints for the value.
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.
That's the value of the temporary is neither of class type nor pointer type nor array type. But even though the temporary object satisfies those constraints, it's not a permitted result of a constant expression because it's not an object with static storage duration. Hence, the temporary is not glvalue core constant expression and therefore is not a constant expression.
Therefore, the variable rf is not constant-initialized and therefore is not usable in constant expression, therefore (4.7) is failed and therefore the temporary bound by rf is also not usable in constant expressions, and therefore (5.8.1) is failed.
Then, (5.8.2) is tried: (5.8.2) is succeeded because the lifetime of rf exactly began within the evaluation of func(0). Checking the subsequent bullets is not relevant to the question. But since the above program is not ill-formed, this means all subsequent bullets are satisfied, and therefore func(0) is a prvalue core constant expression and therefore a constant expression.

what is the meaning of the phrase "preceding initialization" in section [expr.const]

constexpr int func(int const& rf){
return rf;
}
int main(){
constexpr int value = func(0);
}
Consider the above code, the variable value shall be initialized by a constant expression, which is func(0), which firstly shall be a core constant expression. To determine whether the expression func(0)is a core constant expression, the following rules will be applied to it, that is:
expr.const#2.7
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
an lvalue-to-rvalue conversion unless it is applied to
[...], 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;
Despite the lvalue-to-rvalue conversion is applied to rf and such conversion satisfied the bullet (2.7.4), however, take a look to the next paragraph, that is:
expr.const#2.11
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
(2.11.1) it is initialized with a constant expression or,
(2.11.2) its lifetime began within the evaluation of e;
I don't know what actually the phrase preceding initialization mean? Does it mean that a variable should be initialized before using it, or it means that in a declaration of a variable shall have an initializer. Anyhow, before applying the lvalue-to-rvalue conversion to glvalue rf, the glvalue rf should be evaluated to determine the identity of an object, which is ruling by:
A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
That means not only bullet [expr.const#2.7] shall be obeyed but also [expr.const#2.11] shall be obeyed.
Because id-expression rf is of reference type. So, in order to make the expression func(0) be a core constant expression, the id-expression rf must have a preceding initialization, and satisfies at least one of bullet (2.11.1) and (2.11.2). In my example, the bullet (2.11.2) is obeyed by rf and the compiler agree that func(0) is a constant expression. The outcome is here, the outcome seems to evidence that preceding initialization means be initialized rather than have an initializer due to the parameter declaration does not have an initializer in the first example.
In order to check such a thought, I test the below code:
constexpr int func(int const& rf){
constexpr int const& v = rf;
return 0;
}
int main(){
static int const data = 0;
constexpr int value = func(data);
}
The outcomes of the compilers point out rf is not a constant expression. I'm confuse at this outcome. According to the above supposition. rf has a preceding initialization, and the bullet (2.11.1) is obeyed because data is a constant expression, even if the bullet (2.11.2) does not be satisfied.
So, I wonder what actually does the phrase preceding initialization mean? If it means that a declaration for a variable has an initializer, how could the expression func(0) in the first example be a constant expression?
The outcome seems to evidence that "preceding initialization" means "be initialized" rather than have an initializer due to the parameter declaration does not have an initializer in the first example.
It does mean "be initialized", but it's more importantly about the visibility of a preceding initialization within the context of the expression being evaluated. In your example, in the context of evaluating func(0) the compiler has the context to see the initialization of rf with 0. However, in the context of evaluating just the expression rf within func, it doesn't see an initialization of rf. The analysis is local, as in it doesn't analyze every call-site. This leads to expression rf itself within func not being a constant expression while func(0) is a constant expression.
If you were to write this instead:
constexpr int func(int const& rf) {
/* constexpr */ int const& v = rf;
return v;
}
int main() {
static int const data = 0;
constexpr int value = func(data);
}
This is okay again because in the context of func(data), rf has a preceding initialization with a constant expression data, and v has a preceding initialization with rf which is not a constant expression but its lifetime began within the evaluation of func(data).
This is CWG2186:
2186. Unclear point that “preceding initialization” must precede
Similar to the concern of issue 2166, the requirement of 8.20 [expr.const] bullet 2.7.1 for
— 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
does not specify the point at which the determination of “preceding initialization” is made: is it at the point at which the reference to the variable appears lexically, or is it the point at which the outermost constant evaluation occurs? There is implementation divergence on this point.
But the meaning should be "lexically", because ODR.

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 GCC correct in requiring the constexpr specifier for this reference declaration?

The code below doesn't compile under GCC 5.3.0 because the declaration of r is missing a constexpr specifier.
const int i = 1;
const int& r = i;
constexpr int j = r;
I believe the rejection is correct. How do I prove it using the working draft N4527?
First, since we're using a reference, [expr.const]/(2.9) must not be violated. (2.9.1) applies, though:
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
I.e. using r is fine, as long as the initializer - i - is a constant expression (this is shown below).
It's also necessary to check whether the l-t-r conversion in line 3 is legal, i.e. (2.7) must not be violated. However, (2.7.1) applies:
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
…so that's fine as well, since the (g)lvalue is r, and it refers to i - which is a non-volatile const object with a constant expression initializer (1).
We postponed showing that i is actually a constant expression, and once that's out of the way, we need to show that r is a constant expression.
[expr.const]/5 pertains to that:
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.
Since i is, in the above context, a (g)lvalue, it has to be a permitted result of a constant expression - which it is, since it has static storage duration and certainly isn't a temporary. Thus i is a constant expression.
r is, however, treated as a prvalue in line 3. Since we already established that r is a core constant expression, we solely need to check the bullet points. They're clearly met, though.
Hence the code is well-formed in namespace scope. It won't be in local scope, as i wouldn't be a permitted result of a constant expression anymore. Clang gives a comprehensive error message.