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++ }; 
12
  • 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... Commented Sep 6, 2016 at 3:06
  • @πάντα ῥεῖ: Hmmm...that duplicate question (Why are these constructs (using ++) undefined behaviour?) 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. Commented Sep 6, 2016 at 3:15
  • @JonathanLeffler I think here's the better one: stackoverflow.com/questions/4176328/… : Should we reopen/close? Commented Sep 6, 2016 at 3:19
  • 1
    This question is a prime example of why asking a c and c++ question is a bad idea. They are different languages that share a restriced common sublanguage and limited ABI compatibility. Commented Sep 6, 2016 at 13:59
  • 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. Commented Sep 6, 2016 at 15:15

3 Answers 3

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 ]

Sign up to request clarification or add additional context in comments.

4 Comments

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.
@JonathanLeffler Looking up in standard is always challenging. :) Anyway I found it and added to the answer.
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.
@JonathanLeffler Yes, I suppose the question is focused on braced-init-list (list-initialization in C++11).
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.

3 Comments

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: 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.
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.
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.

2 Comments

Hm ... but it's not well defined either, or? Doesn't an unspecified order of side effects basically mean UB, too?
@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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.