Consider the following code:
struct Foo{
std::string s1;
std::string s2;
};
int main(){
Foo f{.s1 = "s1", .s2 = f.s1 + "s2"};
std::cout << "s1='" << f.s1 << "', s2='" << f.s2 << "'" << std::endl;
}
Especially note that the initialization of s2
accesses the first member of f
: .s2 = f.s1 + "s2"
.
The latest clang, gcc and MSVC are happy with the code and give the naively expected result (they print "s1='s1', s2='s1s2'"
). See live on godbolt.
Question: Is this legal?
In other words, does the standard guarantee that f.s1
gets initialized before the designated initializer .s2
is evaluated?
Related: There is a similar question asking about whether .s2 = .s1 + "s2"
is legal, which clearly isn't, because it does not compile. Also, P0328 (as per this answer) might be relevant, but I can't see my question being answered there.
In C++, the order is important. Member variables are initialized in order of declaration.
Initializer lists, designated or not, follow this rule.
In your case, s1
is declared before s2
in Foo
. Consequently, it will be initialized first.
If you had done the opposite (i.e. define s1
based on s2
) it would have been ill-formed Undefined Behaviour since you would have used a yet uninitialized member.
From 9.4.5 List-initialization (§3.1):
If the braced-init-list contains a designated-initializer-list and T is not a reference type, T shall be an aggregate class. The ordered identifiers in the designators of the designated-initializer-list shall form a subsequence of the ordered identifiers in the direct non-static data members of T. Aggregate initialization is performed ([dcl.init.aggr]).
Then from 9.4.2 Aggregates (§7):
The initializations of the elements of the aggregate are evaluated in the element order. That is, all value computations and side effects associated with a given element are sequenced before those of any element that follows it in order.