The following program compiles successfully with all major compilers:
struct S {
constexpr S(const S&){};
constexpr S() = default;
};
int main(void) {
S s1{};
constexpr S s2{ s1 };
}
The rule governing the initialization of constexpr
variables is [dcl.constexpr]/10: (emphasis mine)
A
constexpr
specifier used in an object declaration declares the object as const. Such an object shall have a literal type and shall be initialized. In anyconstexpr
variable declaration, the full-expression of the initialization shall be a constant expression (7.7). Aconstexpr
variable shall have constant destruction.
Per the bold part, the full-expression of the initialization shall be a constant expression. Per my understanding, the full-expression here is the init-declarator per [into.execution]/5:
A full-expression is
- [..] (5.4) an init-declarator ([dcl.decl]) [..]
Per the grammar of init-declarator, the init-declarator is a declarator followed by an initializer:
init-declarator:
declarator initializer
Given this information, we can conclude that the full-expression of the initialization constexpr S s2{ s1 };
is s2{ s1 }
where s2
is a declarator and { s1 }
is an initializer.
Now, [dcl.constexpr]/10 tells us that the full-expression (which is s2{ s1 }
init-declarator) shall be a constant expression. And I've stucked at this point. Per [expr.const]/11, a constant expression is either a glvalue or prvalue core constant expression (with some additional constraints).
For the above example, I found myself reading [dcl.constexpr]/10 as: "the full-expression s2{ s1 }
shall be a glvalue or prvalue core constant expression". So how the init-declarator s2{ s1 }
can be a glvalue or prvalue core constant expression? As you can see, my interpretation led me to a wrong theory. So, What did I misread/conflate here?
Intuitively, I just see the init-declarator s2{ s1 }
is a copy constructor call with s1
as an argument. But that is not the problem I'm trying to understand. Also, why the above program is well-formed is not what I'm trying to ask for. Instead, I need to know why my interpretation of this rule has not brought us to a reasonable conclusion.
EDIT
Note, the same problem we will encounter with this simple example (Demo):
int main(void) {
static const int i = 42;
constexpr int j = i;
constexpr const int &r = i;
}
The full-expression of the initialization constexpr int j = i;
is the init-declarator j = i
, where j
is a declarator and = i
is an initializer So, How can the full-expression j = i
be a prvalue core constant expression?
The full-expression of the initialization constexpr const int &r = i;
is the init-declarator &r = i
, where &r
is a declarator and = i
is an initializer. So, How can the full-expression &r = i
be a glvalue core constant expression?
There's obviously a wording gap in the standard. Your understanding is correct: the standard requires the "full-expression of the initialization" of a constexpr
variable to be a "constant expression", and the standard also states that a "constant expression" is either a "glvalue core constant expression" or a "prvalue core constant expression" (each of which must satisfy some further requirements) so it seems that the standard is telling us that the full-expression of an initialization, which is not a true expression and therefore can never be a glvalue or a prvalue (since value categories are only applicable to expressions), is neither a "glvalue core constant expression" nor a "prvalue core constant expression" and is, therefore, not a "constant expression" at all. This interpretation is obviously not intended, because it would make it impossible to have any constexpr
variables at all.
In practice, compilers seem to take the interpretation that, solely for the purposes of [expr.const]/11:
r
is a glvalue designating r
, ando
is a prvalue that stores into its result object the value of o
, andAny other interpretation would be highly counterintuitive and lead to absurdities.
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