As far as I know, evaluating an expression X means determining what's the value that the expression X yield.
But I have a question about when is an expression is evaluated? Specifically, when are the expressions of a class type are evaluated?
For example:
struct S
{
int x = 42;
int& ref;
S(): ref(x) // is the expression 'ref' evaluated in this context?
{
this->x = 10; // is the expression 'this' evaluated in this context?
};
};
int main()
{
S a{ };
S b{ a }; // is the expression 'a' evaluated in this context?
// if yes, what is the value that the expression 'a' yield?
}
I want to know the formal theory behind expression evaluation.
In C++ we don't say the first expression is evaluated and then the second expression. We say "every value computation and side effect associated with the first expression is sequenced before every value computation and side effect associated with the second expression", and I think that's beautiful. --me, just now
There is not a completely satisfying answer to the general question of "when is an expression evaluated?" However, I will try to provide an answer to your question because I believe that if people see that this question doesn't have an answer, they will think that C++ is too complicated to be worth using.
The order of evaluation of expressions in a C++ program is not specified through any kind of formal description, but must be pieced together and often inferred from various different kinds of phrasings that are not as explicit as you might hope.
For example, what happens when you call a function? The standard specifies in [expr.call] that each parameter is initialized with its corresponding argument (p7) but where does it specify that, after this has been done, the first statement in the body of the function is executed? The closest thing we have is [intro.execution]/11:
To be honest, this is as clear as mud. What are we to understand from "before execution of every expression or statement in the body of the called function"? Does it mean that after the parameters are initialized from the argument expressions and the postfix expression designating the called function is evaluated, every expression or statement in the body is executed? No, it doesn't; after all, control flow constructs can cause some statements to be skipped. So how do we even know that the starting point is the first statement (after which either control flow constructs or, in their absence, the lexical ordering of statements determines what happens next)? You sort of just have to infer it: if the starting point were the second statement, it would violate [stmt.pre]/1, which states that "except as indicated, statements are executed in sequence". (This is not very clear. One could easily argue that it only means that if both statements are executed, then the lexically first one gets executed first, but that's not the same as saying that if the second one gets executed, the first one must have been executed before.)
If you are looking for "the formal theory behind expression evaluation", I feel that you will be sorely disappointed.
All right, let's assume the things that we know to be obvious, and I'll address the specifics in your question.
Is
aevaluated in the declaration ofb? Yes it is. Because the standard states that "executing a program starts a main thread of execution in which themainfunction is invoked" ([basic.start.main]/1), and we can assume (see above) that this means the declaration statement forawill be evaluated, then the declaration statement forb. (As above, [stmt.pre]/1 states that "except as indicated, statements are executed in sequence". Thanks to Nicol Bolas for pointing this out.)The meaning of the declaration statement for
bis given by [stmt.dcl]/2:So
b, having automatic storage duration, is initialized. The meaning of this initialization is given by [dcl.init.general]/17.1, which states that the object is list-initialized, and this then takes us to [dcl.init.list]/3.9:This is a direct-list-initialization, so
bis direct-initialized froma. For the meaning of this, we have to go back to [dcl.init.general]/17.6.2:This results in the call to
S's implicitly declared copy constructor, which is specified elsewhere in the standard to have the same behaviour asA function call results in the initialization of the parameters from the corresponding arguments ([expr.call]/7), so
otheris initialized froma. [dcl.init.general]/15 specifies that the type of initialization this performs is copy-initialization. [dcl.init.ref]/5.1 governs this initialization:This implies evaluation of
a, because if it's not evaluated, then we wouldn't know what lvalue to bind the reference to. This is another example of how the fact that something even is evaluated generally has to be inferred because it is not stated as explicitly as one might hope. The result of evaluatingais given by [expr.prim.id.unqual]/2:That is, the result of the evaluation of the expression
ais "lvalue designating the object nameda".In
S(): ref(x),refis not an expression, so it is not evaluated. The entire constructref(x)is known as a mem-initializer and will be evaluated if the constructor is called; this is specified by [class.base.init]/13:Such initialization of non-static data members is done according to [class.base.init]/7:
That is, when the constructor is called, and before the outermost block of the constructor is entered,
refis initialized according to the mem-initializer. This initialization is direct-initialization withxas the initializer.Finally, in the body of
S's default constructor, based on the previously discussed considerations, the statementthis->x = 10;will be evaluated if that constructor is called. It is an expression statement. [stmt.expr]/1 says:The meaning of a discarded-value expression is given by [expr.context]/2:
The expression
this->x = 10is a glvalue, so it will be evaluated and its value discarded. Specifically, it is an assignment expression, and [expr.ass]/1 states thatThis states that the actual assignment occurs after both the left and right operands have been evaluated (the "value computation"). This implies that
this->xis evaluated. It is a class member access expression, and [expr.ref]/1 states that "the postfix expression before the dot or arrow is evaluated". That expression isthis, consequently, we conclude thatthisis evaluated.