17

Edit: Not already answered - the linked question was about ordinary r-values, initializer lists are a separate, if related concept.

Is this statement well-defined, or is using the prefix increment operator in an initializer list, on a variable that appears twice in the list, undefined behavior?

struct T t = { i, ++i };

I'm most interested in ANSI C, but it'd also be useful to know if other versions of C and/or C++ differ. And if similar constructs like the following are legal:

struct T t = { i, i++ };

struct T t = { ++i, ++i };

struct T t = { i++, ++i };

struct T t = { i++, i++ };
Ray Hamel
  • 1,289
  • 6
  • 16
  • 6
    There are no sequence points associated with components of an initializer list, so the behaviour is undefined. Now to go find the part of the standard that says that... – Jonathan Leffler Sep 06 '16 at 03:06
  • @πάντα ῥεῖ: Hmmm...that duplicate question ([Why are these constructs (using ++) undefined behaviour?](http://stackoverflow.com/questions/949433/why-are-these-constructs-using-undefined-behavior)) covers a lot of ground, but AFAICS, it doesn't cover the sequencing of initializations, which could be different. Specifically, the section of the standard covering the behaviour is not the same as any of those quoted in any of the answers to that duplicate. – Jonathan Leffler Sep 06 '16 at 03:15
  • @JonathanLeffler I think here's the better one: https://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points : Should we reopen/close? – πάντα ῥεῖ Sep 06 '16 at 03:19
  • Clearly not an exact duplicate of either of those. Find something that specifically has initializer lists, or write an answer. – M.M Sep 06 '16 at 03:19
  • That's C++; this question is dual-tagged (drat it!) but the preference is for C. – Jonathan Leffler Sep 06 '16 at 03:20
  • This question is a prime example of why it was a bad idea for languages to introduce ++ and -- – Bradley Thomas Sep 06 '16 at 13:23
  • 1
    This question is a prime example of why asking a [tag:c] and [tag:c++] question is a bad idea. They are different languages that share a restriced common sublanguage and limited ABI compatibility. – Yakk - Adam Nevraumont Sep 06 '16 at 13:59
  • @Yakk Speaking as the OP, I think it's quite useful to shed light on the more obscure corner cases of C/C++ (in)compatibility, such as this one. That's why I asked about both languages. – Ray Hamel Sep 06 '16 at 14:49
  • @RayHamel Sure, but notice *no one of the answers below actually answered your question*. The best you got was an answer for each language, and someone saying "another answer handles the C++ case". The fact you stated you mainly cared about one half of the two languages also harms the quality of the answers in my experience. – Yakk - Adam Nevraumont Sep 06 '16 at 14:54
  • @Yakk Perhaps I'm missing something, but AFAICT my question was answered, with citations — it's undefined in C and well-defined (evaluated left to right) in C++11. Both these things are helpful for C programmers or those working on a mixed codebase; many C compilers likely implement the C++ behavior, but one can't rely on it. I probably should not have said I was "mainly interested" in C89, yes — I wasn't expecting more than one or two responses. – Ray Hamel Sep 06 '16 at 15:12
  • 1
    @RayHamel There where multiple answers that collectively answered your question. No one answer answered your entire question. One of the goals of SO is to produce questions which are answered with one definiative answer: a slurry of partial answers is better than nothing, but not ideal. – Yakk - Adam Nevraumont Sep 06 '16 at 15:15
  • @JonathanLeffler for C++ also see [Are multiple mutations within initializer lists undefined behavior?](http://stackoverflow.com/q/14442894/1708801) and [Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11](http://stackoverflow.com/q/19881803/1708801) – Shafik Yaghmour Sep 12 '16 at 05:27

3 Answers3

14

C++11 and later

The behavior is well-defined for list initialization. According to the sequenced-before rules (since C++11):

10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.

So for struct T t = { i, ++i };, i will be evaluated at first, and then ++i, the order is well-defined. And all the other samples would be fine too.

Quotes from the C++ standard, $8.6.4/4 List-initialization [dcl.init.list]:

(emphasis mine)

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. — end note ]

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Reading the actual standard, it is quite hard to work out where that is stated. Section §8.5 Initializers of C++11 is both lengthy and hard to read. – Jonathan Leffler Sep 06 '16 at 03:53
  • @JonathanLeffler Looking up in standard is always challenging. :) Anyway I found it and added to the answer. – songyuanyao Sep 06 '16 at 04:04
  • That chunk was what I found — and it is too long to fit in a single comment (so I didn't add it in comments). There are previous subsections that also discuss initialization (8.5.1 to 8.5.3, plus 8.5 itself) — but since the syntax in the question is using `{ … }` I guess 'list initialization' is the relevant section. – Jonathan Leffler Sep 06 '16 at 04:05
  • @JonathanLeffler Yes, I suppose the question is focused on braced-init-list (list-initialization in C++11). – songyuanyao Sep 06 '16 at 04:08
13

C

In C (not necessarily the same answer as for C++), there are no sequence points associated with the components of an initializer list.

The C11 standard, ISO/IEC 9899:2011, says in section §6.7.9 Initialization:

¶19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; 151)

151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.

That sounds promising, but…

¶23 The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.152)

152) In particular, the evaluation order need not be the same as the order of subobject initialization.

So, (in C) the order of evaluation is indeterminately sequenced, and you cannot rely on when the increments occur (or, in extreme cases not illustrated by the code in the question, whether the increments occur).

In C99 (ISO/IEC 9899:1999), the section number is §6.7.8, but paragraphs 19 and 23 have essentially the same content, except that the footnote numbers are different.

In C90 (ISO/IEC 9899:1990), the issue is not addressed explicitly.

C++

Judging from songyuanyao's answer, the rules in C++11 (and later) are different from those in C11. This sort of thing emphasizes that the languages C and C++ are different and makes writing comprehensive answers to questions tagged with both languages extremely difficult.

Closely related questions

There are at least two other questions related to side-effects (such as ++) in contexts other than initializers. They both should be read too. The second, in particular, is of interest to C++ users; the first is tagged C and not C++ and so is of most relevance to those interested in C.

Both were pointed out by πάντα ῥεῖ in the comments.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Para 23 in C99+TC2 does not mention sequencing or sequence points, so my interpretation would be that OP code is undefined behaviour. – M.M Sep 06 '16 at 03:55
  • @M.M: In my copy of C99 (original), it says: _§23 The order in which any side effects occur among the initialization list expressions is unspecified.130)_. I looked at each of TC1, TC2, TC3 and this text doesn't change directly. Strictly, 'unspecified' is different from 'undefined behaviour', but the net result is, I think, essentially the same. – Jonathan Leffler Sep 06 '16 at 04:03
  • The order of side-effects on the operands of `+` is unspecified too. But it is still undefined if multiple of those side effects write the same object. It doesn't say anywhere that there is a sequence point, so it should be assumed that there is not IMHO. – M.M Sep 06 '16 at 04:11
6

In C11 the behaviour of all these initialization is not undefined. See 6.7.9/23:

The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

The term indeterminately sequenced is defined as such (5.1.2.3):

Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B, but it is unspecified which.

In C99 the language used was not clearly worded as to whether it is the same situation, or undefined behaviour. In C89 the issue is not mentioned at all, so we should probably assume that in C89 these are undefined.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Hm ... but it's not well defined either, or? Doesn't an unspecified order of side effects basically mean UB, too? – Daniel Jour Sep 06 '16 at 14:36
  • @DanielJour It is UB only if the side-effects are *unsequenced*. But in this case they are *indeterminately sequenced*. I avoided using the term "well-defined" because sometimes people use that to mean not even any unspecified behaviour. – M.M Sep 06 '16 at 22:00