11

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"

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 2
    See: https://www.securecoding.cert.org/confluence/display/seccode/DCL04-C.+Do+not+declare+more+than+one+variable+per+declaration – Damon Jun 14 '14 at 20:46
  • 1
    @Deduplicator Why not? Isn't each declarator sequenced? – juanchopanza Jun 14 '14 at 21:10
  • 3
    Related? http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#1342 – dyp Jun 14 '14 at 23:36
  • @Damon: That's the stupidest thing I've seen all day. "Don't write code because you will confuse people who don't understand code", it may as well say. – Lightness Races in Orbit Jun 15 '14 at 00:44
  • 1
    @LightnessRacesinOrbit: given that many people argue that the code in the OP's question hss undefined behavior, is a code-style standard that requires separate declarations really all that idiotic? – Michael Burr Jun 15 '14 at 05:44
  • @LightnessRacesinOrbit: On the contrary. Unless you never plan to work with another human, you have to plan for the possibility that someone else is slightly less genious than you are and _does_ understand the intent of your code wrong. If your code lends to that, you're a bad coder. It's the same principle as with using redundant parenthesis. Because, hey, you know operator precedence, and everybody who doesn't is a loser, so there is no real need, is there. But truth is, if others can't identify your intent correctly at the first glance, it's your failure, not theirs. – Damon Jun 15 '14 at 13:02
  • 1
    @Damon: I plan to work with other humans _who are programmers_. – Lightness Races in Orbit Jun 15 '14 at 18:39
  • @LightnessRacesinOrbit: That's nice, but... _programmers_ like e.g. the guy who literally invented the C++ language suggest that you use one declaration per line and that while you should know the most basic precedence rules, you should parenthese them to avoid any doubt. They suggest that because they realize that even _they_ don't remember every rule 100% correctly at all times, and in particular someone else who might not have 4 decades of experience might not. – Damon Jun 15 '14 at 19:09
  • @Damon: Appeal to authority for style guides; awesome. BTW, while Bjarne originally invented "C with Classes" and developed it into what later became pre-standard C++, _hundreds of people_ created the C++ we know today. Just so you're aware. – Lightness Races in Orbit Jun 16 '14 at 09:57
  • I updated my answer extensively, this seems to be unspecified using the current language, although. – Shafik Yaghmour Jun 16 '14 at 15:42
  • @ShafikYaghmour It seems like they are. – juanchopanza Jun 16 '14 at 18:42
  • @juanchopanza well more precisely, I can not find a normative reference that proves they are although it seems more than likely they are meant to be. – Shafik Yaghmour Jun 16 '14 at 19:23

2 Answers2

9

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.

Community
  • 1
  • 1
101010
  • 41,839
  • 11
  • 94
  • 168
  • 2
    Footnotes are non-normative. I think the *usually* in the footnote leaves enough wiggle room for an implementation to evaluate them in the reverse order, and still be compliant. – Praetorian Jun 14 '14 at 23:25
  • @Praetorian I agree with the non-normativeness of footnotes, but the quote from the standard clearly states they're analysed separately. – 101010 Jun 14 '14 at 23:37
  • Yes, that's very clear. But evaluating `T D1, D2, ... Dn;` as `T Dn; ... T D2; T D1;` is also analyzing them separately. – Praetorian Jun 14 '14 at 23:41
  • @Praetorian isn't comma sequential evaluation left to right? – 101010 Jun 14 '14 at 23:44
  • 3
    The *comma operator* is. But this is not the comma operator, here's it just a separator for *init-declarators* - §8/1 The *init-declarator-list* appearing in a declaration is a comma-separated sequence of declarators – Praetorian Jun 14 '14 at 23:48
  • 1
    "usually equivalent, not strictly" - so you are saying that there is a possibility that `y` will not be initialized to `42`? – M.M Jun 15 '14 at 00:35
  • @MattMcNabb interpretation of the standard implies that yes `y` might not be initialized to 42. It depends on how the order of the evaluation is implemented. – 101010 Jun 15 '14 at 00:38
  • But, if the order of initialisation would not be granted, could the compiler really chose int y=x;int x=42 as alternative (x would be undefined) ? Or otherwhise stated, could int y=x, x=42 be a valid expression ? – Christophe Jun 16 '14 at 18:53
  • @Christophe `y = x, x = 42` is not a valid expression. As such the compiler in this case would generate an error. – 101010 Jun 16 '14 at 19:28
  • True: it's not valid if the order is from left to right. Clang++ warns because x would be uninitialised, and Visual C++ does not even compile it because he states that the x is not yet defined. – Christophe Jun 16 '14 at 19:42
  • @MattMcNabb well since that text is not normative we can not prove or disprove anything based on it. Based on the text as I say it looks unspecified. – Shafik Yaghmour Jun 17 '14 at 15:32
5

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.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I like it much better now that it gets into the details of what the Standard says. Yet, I'm not sure how you refute my original criticism: The initializer is an expression, not the initialization. – dyp Jun 16 '14 at 13:50
  • The initializer is a declarator not an expression since it does not come up section `5` but it can contain and expression and each expression will be a full expression since it is not a sub-expression. – Shafik Yaghmour Jun 16 '14 at 13:53
  • Yes exactly, that's my whole point :) where does the Standard guarantee that initialization is done in order? – dyp Jun 16 '14 at 13:55
  • See also [CWG active #1342](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#1342), which I linked in comments to the OP. – dyp Jun 16 '14 at 13:57
  • But the initialization of `x` is not part of the full-expression `42` neither a side effect of the expression, is it? – dyp Jun 16 '14 at 14:08
  • @dyp I agree, the order of evaluation is unspecified, updated answer. – Shafik Yaghmour Jun 16 '14 at 15:37
  • I think it *should* be defined. So either we're missing something, or the Standard's too vague. Either way, maybe a question on the Standard's mailing list could help. – dyp Jun 16 '14 at 15:40
  • @dyp I wonder if section `3.3.2 p1` implies the ordering, I could see how it could be read that way. The subsequent paragraphs also read as if an order if implied. – Shafik Yaghmour Jun 16 '14 at 20:04
  • `int x = 5; int y = x;` *must* be well-defined behaviour. It seems this is only specified via [stmt.stmt]/1 "Except as indicated, statements are executed in sequence." which does not apply to `int x = 5, y = x;` -- I would say that 3.3.2/1 mostly specifies name lookup, not initialization. But I agree it's probable that the *intention* is to sequence multiple init-declarators. – dyp Jun 16 '14 at 20:33