Is an init-declarator a prvalue expression - c++

int c = 0;
Consider the above code,thereof,c = 0 is an init-declarator and it's also an expression,Becuase of these rules:
init-declarator:
declarator initializer(opt)
A full-expression is:
[...]
an init-declarator or a mem-initializer, including the constituent expressions of the initializer,
As long as an expression,it will have a value category.
A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator, as specified by the context in which it appears.
The evaluation of a = 0 will initialize object a.So, Is the full-expression c=0 a prvalue expression?If I misunderstand it,please correct me.

Consider the above code,thereof,c = 0 is an init-declarator and it's also an expression
That's not how C++ parsing works. c = 0 by itself may be an expression (if it is within a context where expressions are allowed), but that's not how int c = 0; gets parsed. You have to follow the actual C++ grammar rules.
int c = 0; is a simple-declaration, containing a decl-specifier-seq and an optional init-declarator-list. The latter is a sequence of one or more init-declarator terms. And this grammar has two components: a declarator and an optional initializer. Grammatically speaking, the decl-specifier-seq is where int goes, the declarator is the c part, and the initializer is the = 0 bit.
The text of an init-declarator is something that may in some cases be parsed as an expression. But what something is parsed as is determined by the grammar rules. And the grammar rules of simple-declaration does not allow a decl-specifier-seq followed by expression. Therefore, what follows it is not parsed as an expression even if it could be.
So init-declarator is not an expression, even if the text looks like it could be.
Now, there is the concept of a "full-expression". One of the things that get to be called a "full-expressions" are init-declarator grammar.
The part that's confusing you is the difference between a "full-expression" and an expression. An expression is a specific piece of C++ grammar. A full-expression is not; it's a language concept which includes a number of different pieces of grammar, but full-expression is not itself grammar.
Therefore, while the grammatical construct init-declarator is a "full-expression" that does not make it an expression. The grammar construct expression is well defined, and int c = 0; doesn't fit that grammar. The init-declarator may contain an expression (or multiple expressions, depending on the initializer), but it is not itself an expression.
And only expressions have value categories. Therefore, asking about the value category of a thing which is not an expression is not a valid question.

Related

Initialization of a constexpr variable

[dcl.constexpr] p10 sentence 3 says:
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression
However, in this declaration:
constexpr int a = 10;
constexpr int b = a;
a is not a constant expression, as it is glvalue core constant expression, but not a permitted result of a constant expression because it does not have static storage duration, and is not a temporary object.
However, with the application of an lvalue-to-rvalue conversion, it will become a constant expression. So does that mean that the initializer does not need to be a constant expression, and only the final result after conversions has to be?
In the link you quoted, see point 10:
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:
Your question focused on "permitted result of a constant expression" under the glvalue branch; however in your example it is the other branch "prvalue core constant expression" that applies. This can apply because of [conv.lval]/1,
A glvalue of a non-function, non-array type T can be converted to a prvalue
I agree it is a bit confusing that by "prvalue core constant expression" here they include the case of the result of lvalue-to-rvalue conversion on glvalues that meet the criteria; whereas in some other places in the standard "prvalue" excludes that case.
The word "full-expression" is a defined term. Notably ([intro.execution]/5)
Conversions applied to the result of an expression in order to satisfy the requirements of the language construct in which the expression appears are also considered to be part of the full-expression.
So yes, since the requirement says "the full-expression of the initialization shall be a constant expression", it means only the full-expression (which includes the conversion), not anything else, is required to be a constant expression.

Difference between expression result and what an expression denotes

[basic.lval] p5 states:
The result of a glvalue is the entity denoted by the expression.
I have a few questions regarding this and it's implications:
What is the entity denoted by the expression?
Would this differ from the result?
[expr.type] p1 states:
The expression designates the object or function denoted by the reference
So for example, given the declaration foo f; where foo is a class type, would the expression f; also be an identifier, and that's what it means by the expression denoting an entity, since the expressions name corresponds to the identifier?
Pretty much what I'm asking is, what does it mean for an expression to denote an entity, and how does it differ from the result?
I suppose that [basic.lval]/5 basically say that the concept of naming/denoting/designating and "result of" are used interchangeably in the standard.
The result of a glvalue is the entity denoted by the expression.
The result of a prvalue is the value that the expression stores into its context; a prvalue that has type cv void has no result.
A prvalue whose result is the value V is sometimes said to have or name the value V.
A glvalue or a prvalues are expressions [basic.lval]/1. All expressions are evaluated. Even a single name appearing in a expression (not in a declaration), is an expression. Before evaluation an expression may denote an entity and after evaluation the expression result in this entity.
For example, to evaluate the expression a+b, the virtual machine first evaluate a. a denotes some object, the result of the evaluation of a is this object. The same for b. Then the result of the expression a and the result of the expression b will be used (after some conversions) as operands in the evaluation of the addition.
Only the result of an expression is used by other expressions. So it seems natural to conflate an expression for its result. When we do that we forget that the result is the consequence of the evaluation of the expression.

Can I Reference Previous Members of an Initializer List?

Say I want to refer to a member of an initializer_list that I already defined. Can I do it?
This code compiles and gives the expected: "13 55 " in both Visual Studio and gcc, I'd just like to know that it's legal:
const int foo[2] = {13, foo[0] + 42};
So what we have here is aggregate initialization covered in section 8.5.1 of the draft C++ standard and it says:
An aggregate is an array or a class [...]
and:
When an aggregate is initialized by an initializer list, as specified
in 8.5.4, the elements of the initializer list are taken as
initializers for the members of the aggregate, in increasing subscript
or member order. Each member is copy-initialized from the
corresponding initializer-clause [...]
Although it seems reasonable that side effects from initializing each member of the aggregate should be sequenced before the next, since each element in the initializer list is a full expression. The standard does not actually guarantee this we can see this from defect report 1343 which says:
The current wording does not indicate that initialization of a non-class object is a full-expression, but presumably should do so.
and also notes:
Aggregate initialization could also involve more than one full-expression, so the limitation above to “initialization of a non-class object” is not correct.
and we can see from a related std-discussion topic Richard Smith says:
[intro.execution]p10: "A full-expression is an expression that is not
a subexpression of another expression. [...] If a language construct
is defined to produce an implicit call of a function, a use of the
language construct is considered to be an expression for the purposes
of this definition."
Since a braced-init-list is not an expression, and in this case it
does not result in a function call, 5 and s.i are separate
full-expressions. Then:
[intro.execution]p14: "Every value computation and side effect
associated with a full-expression is sequenced before every value
computation and side effect associated with the next full-expression
to be evaluated."
So the only question is, is the side-effect of initializing s.i
"associated with" the evaluation of the full-expression "5"? I think
the only reasonable assumption is that it is: if 5 were initializing a
member of class type, the constructor call would obviously be part of
the full-expression by the definition in [intro.execution]p10, so it
is natural to assume that the same is true for scalar types.
However, I don't think the standard actually explicitly says this
anywhere.
So this is currently not specified by the standard and can not be relied upon, although I would be surprised if an implementation did not treat it the way you expect.
For a simple case like this something similar to this seems a better alternative:
constexpr int value = 13 ;
const int foo[2] = {value, value+42};
Changes In C++17
The proposal P0507R0: Core Issue 1343: Sequencing of non-class initialization clarifies the full-expression point brought up here but does not answer the question about whether the side-effect of initialization is included in the evaluation of the full-expression. So it does not change that this is unspecified.
The relevant changes for this question are in [intro.execution]:
A constituent expression is defined as follows:
(9.1) — The constituent expression of an expression is that expression.
(9.2) — The constituent expressions of a braced-init-list or of a (possibly parenthesized) expression-list are the
constituent expressions of the elements of the respective list.
(9.3) — The constituent expressions of a brace-or-equal-initializer of the form = initializer-clause are the
constituent expressions of the initializer-clause.
[ Example:
struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };
The constituent expressions of the initializer used for the initialization of b are 5 and 1+1. —end example ]
and [intro.execution]p12:
A full-expression is
(12.1) — an unevaluated operand (Clause 8),
(12.2) — a constant-expression (8.20),
(12.3) — an init-declarator (Clause 11) or a mem-initializer (15.6.2), including the constituent expressions of the
initializer,
(12.4) — an invocation of a destructor generated at the end of the lifetime of an object other than a temporary
object (15.2), or
(12.5) — an expression that is not a subexpression of another expression and that is not otherwise part of a
full-expression.
So in this case both 13 and foo[0] + 42 are constituent expression which are part of a full-expression. This is a break from the analysis here which posited that they would each be their own full-expressions.
Changes In C++20
The Designated Initialization proposal: P0329 contains the following addition which seems to make this well defined:
Add a new paragraph to 11.6.1 [dcl.init.aggr]:
The initializations of the elements of the aggregate are evaluated in the element order. That is,
all value computations and side effects associated with a given element are sequenced before those of any element that follows it in order.
We can see this is reflected in the latest draft standard.

Interdependent initialization with commas?

Is the following perfectly defined:
int x = 42, y = x;
i.e. strictly equivalent to:
int x = 42;
int y = x;
EDIT : the question is not about style (I know that it's wrong...), the question is "theoretical"
The correct answer is that
int x = 42, y = x;
and
int x = 42;
int y = x;
are usually equivalent (not strictly).
Considering the standard § 8 Declarators [dcl.decl]:
3 Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.
and in the footnote [100] further explains:
A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single
declarator. That is
T D1, D2, ... Dn;
is usually equivalent to
T D1; T D2; ... T Dn;
where T is a decl-specifier-seq and each Di is an init-declarator.
The above guarantees that x = 42 and y = x will be evaluated separately. However, as #Praetorian correctly pointed out in the comments, footnotes are not normative.
This means that the order of evaluation is not well defined and an implementer could as well implement the evaluation of the declarations in the reverse order (i.e,. T Dn; ...T D2; T D1;).
One might argue that the comma operator is guaranteed left to right evaluation. However, this not the case. According to the K & R [K & R II, 3.6 p.63], that also applies to C++:
The commas that separate function arguments, variables in declarations, etc., are not comma operators, and do not guarantee left to right evaluation.
This question came up in comp.lang.c++.moderated a long time ago under the topic init-declarator-list analysis order and the conclusion there was Yes.
Although I see the full-expression argument but I do not see the order of evaluation argument. So I think this is unspecified.
The relevant part of the question is:
In this declaration and definition:
int a = 2, b = a;
Is it guaranteed that b will always be initialized as 2 ? If yes, then
can we say that a = 2 is always analysed(or evaluated?) before b = a ?
and the relevant part of the answer is:
Yes. Strictly stated, the observable behavior of the program must be
as if all of the side effects of the 'a = 2' part of the declaration
took place before the evaluation of the 'b = a' part starts. (In
practice, of course, in this simple example, a compiler could assign 2
to both a and b in any order, or even in parallel, because doing so
would result in the same observable behavior.)
and further down:
In this particular case, however, it does separate the declarator
list into separate declarators; each declarator contains a complete
expression, and the declarators are evaluated in order.
Update
What makes each init-declator a full expression is subtle but as far as I can tell follows the same logic I used in Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11. In this case we start from the grammar defined in ection 8:
init-declarator-list:
init-declarator
init-declarator-list , init-declarator
init-declarator:
declarator initializeropt
The next point of focus is the initializer grammar which is covered in section 8.5:
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
initializer-clause:
assignment-expression
braced-init-list
In both cases we have = initializer-clause which bring us to assignment-expression which if we follow the grammar in section 5 bring us back to primary-expression which can give us either a literal or id-expression.
So we do indeed have full-expressions separated by a grammatical comma so we have:
int x = 42, y = x;
^ ^
| end full-expression
end full-expression
and according to section 1.9 paragraph 14 we see that:
Every value computation and side effect associated with a
full-expression is sequenced before every value computation and side
effect associated with the next full-expression to be evaluated.8.
As for the order of evaluation, I think this is not specified, the same logic that applies to defect report 430 for initializer lists would seem to apply here as well. In C++11 the language for initializer lists was fixed with the following addition in section 8.5.4:
Within the initializer-list of a braced-init-list, the
initializer-clauses, including any that result from pack expansions
(14.5.3), are evaluated in the order in which they appear. [...]
there is no such equivalent for initializer.

Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11

Consider the following code:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
If we compile the code using clang -std=c++03 it produces the following warning(live example):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
I am not advocating for code like this but similar code came up in another question and there was disagreement over whether it is defined or not according to the standard pre-C++11. In C++11 this behavior is well defined behavior according to Are multiple mutations within initializer lists undefined behavior and indeed if I use -std=c++11 then the warning goes away.
If we look at a pre-C++11 draft standard it does not have the same language covering initializer-list so it seems we are left with Chapter 5 Expressions paragraph 4 which says:
Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.57) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
In order for this to be undefined it would seem we would have to interpret count++, count++ as an expression and therefore each count++ as a subexpression, so is this code undefined pre-C++11?
The code is not undefined pre-C++11 but the evaluation order is unspecified. If we look at the draft standard section 1.9 Program execution paragraph 12 says:
A full-expression is an expression that is not a subexpression of another expression. [...]
and paragraph 15 says:
There is a sequence point at the completion of evaluation of each full-expression12).
then the question is whether count++, count++ is a full expression and each count++ a sub-expression or is each count++ it's own full expression and therefore there is sequence point after each one? if we look at the grammar for this initialization from section 8.5 Initializers:
initializer-clause:
assignment-expression
{ initializer-list ,opt }
{ }
initializer-list:
initializer-clause
initializer-list , initializer-clause
the only expression we have is an assignment-expression and the , separating the components is part of the initializer-list and and not part of an expression and therefore each count++ is a full expression and there is a sequence point after each one.
This interpretation is confirmed by the following gcc bug report, which has very similar code to mine(I came up with my example way before I found this bug report):
int count = 23;
int foo[] = { count++, count++, count++ };
which ends up as defect report 430, which I will quote:
[...]I believe the standard is clear that each initializer expression in the above is a full-expression (1.9 [intro.execution]/12-13; see also issue 392) and therefore there is a sequence point after each expression (1.9 [intro.execution]/16). I agree that the standard does not seem to dictate the order in which the expressions are evaluated, and perhaps it should. Does anyone know of a compiler that would not evaluate the expressions left to right?