Here's the sample code:
X * makeX(int index) { return new X(index); }
struct Tmp {
mutable int count;
Tmp() : count(0) {}
const X ** getX() const {
static const X* x[] = { makeX(count++), makeX(count++) };
return x;
}
};
This reports Undefined Behavior on CLang build 500 in the static array construction. For sake of simplification for this post, the count is not static, but it does not change anything. The error I am receiving is as follows:
test.cpp:8:44: warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
In C++11, this is fine; each clause of an initialiser list is sequenced before the next one, so the evaluation is well-defined.
Historically, the clauses might have been unsequenced, so the two unsequenced modifications of
count
would give undefined behaviour.(Although, as noted in the comments, it might have been well-defined even then - you can probably interpret the standard as implying that each clause is a full-expression, and there's a seqeuence point at the end of each full-expression. I'll leave it to historians to debate the finer points of obsolete languages.)
Update 2
So after some research I realized this was actually well defined although the evaluation order is unspecified. It was a pretty interesting putting the pieces together and although there is a more general question covering this for the C++11 case there was not a general question covering the pre C++11 case so I ended up creating a self answer question, Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11 that covers all the details.
Basically, the instinct when seeing
makeX(count++), makeX(count++)
is to see the whole thing as a full-expression but it is not and therefore each intializer has a sequence point.Update
As James points out it may not be undefined pre-C++11, which would seem to rely on interpreting the initialization of each element as a full expression but it is not clear you can definitely make that claim.
Original
Pre-C++11 it is undefined behavior to modify a variable more than once within a sequence point, we can see that by looking at the relevant section in an older draft standard would be section
5
Expressions paragraph 4 which says (emphasis mine):In the C++11 draft standard this changes and to the following wording from section
1.9
Program execution paragraph 15 says (emphasis mine):and we can see that for initializer lists from section
8.5.4
List-initialization paragraph 4 says:Because it this case, the
,
is NOT a sequence point, but acts more like a delimiter in the initialization of the elements of the array.In other words, you're modifying the same variable twice in a statement without sequence points (between the modifications).
EDIT: thanks to @MikeSeymour: this is an issue in
C++03
an before. It seems like inC++11
, the order of evaluation is defined for this case.