150

Consider:

struct Person
{
    int height;
    int weight;
    int age;
};

int main()
{
    Person p { .age = 18 };
}

The code above is legal in C99, but not legal in C++11.

What was the standard committee's rationale for excluding support for such a handy feature?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 11
    It apparently didn't make sense to the design committee to include it, or it simply didn't come up in the meetings. It's worth noting that C99 designated initializers are *not in any* of the C++ specification versions. Constructors seem to be the preferred initialization construct, and for good reason: they guarantee consistent object initialization, if you write them correctly. – Robert Harvey Sep 11 '13 at 02:27
  • 21
    Your reasoning is backward, a language need not have a rationale for *not having* a feature, it needs a rationale for having one and a strong one at that. C++ is bloated enough, as it stands. – Matthieu M. Sep 11 '13 at 07:06
  • 52
    A good reason (which cannot be solved with constructors except by writing stupefying wrappers) is that whether or not you use C++, most real APIs are C, not C++, and not few of them make you supply a structure in which you want to set one or two fields -- and not necessarily the first -- but need to have the rest zero-initialized. Win32 API `OVERLAPPED` is such an example. Being able to write `={.Offset=12345};` would make code much clearer (and probably less error-prone). BSD sockets are a similar example. – Damon Nov 04 '13 at 12:36
  • 2
    Looks like this has just been proposed: http://htmlpreview.github.io/?https://raw.github.com/CTMacUser/multiarray-iso-proposal/master/designation-proposal.html – Josh Haberman Nov 11 '13 at 21:47
  • 17
    The code in `main` is not legal C99. It should read `struct Person p = { .age = 18 };` – chqrlie May 05 '15 at 22:54
  • @chqrlie That's also not legal C99. It would have to read `struct Person p = (struct Person) { .age = 18 }; `. If you don't declare the type of the initializer (which looks like a cast), then you're using a non-standard extension. – Theodore Murdock Feb 08 '16 at 23:40
  • 5
    @TheodoreMurdock: I'm afraid you are mistaken, both are legal C99, but different things: `struct Person p = { .age = 18 };` is a definition for `p` with an initializer that uses a designated initializer (C11 6.7.9), `struct Person p = (struct Person) { .age = 18 };` defines `p` with an initializer that is a postfix expression, namely a compound literal of the same type (C11 6.5.2). – chqrlie Feb 09 '16 at 00:21
  • 20
    FYI C++20 will support designated initializers – Andrew Tomazos Nov 06 '18 at 08:44
  • Because https://www.youtube.com/watch?v=IAdLwUXRUvg – alfC Oct 16 '20 at 08:46

5 Answers5

115

On July 15 '17 P0329R4 was accepted into the standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
This brings limited support for 's Designated Initializers. This limitation is described as follows by C.1.7[diff.decl].4, given:

struct A { int x, y; };
struct B { struct A a; };

The following Designated Initializations, which are valid in C, are restricted in C++:

  • struct A a = { .y = 1, .x = 2 } is invalid in C++ because designators must appear in the declaration order of the data members
  • int arr[3] = { [1] = 5 } is invalid in C++ because array designated initialization is not supported
  • struct B b = {.a.x = 0} is invalid in C++ because designators cannot be nested
  • struct A c = {.x = 1, 2} is invalid in C++ because either all or none of the data members must be initialized by designators

For and earlier Boost actually has support for Designated Intializers and there have been numerous proposals to add support to the standard, for example: n4172 and Daryle Walker's Proposal to Add Designation to Initializers. The proposals cite implementation of 's Designated Initializers in Visual C++, gcc, and Clang claiming:

We believe the changes will be relatively straightforward to implement

But the standard committee repeatedly rejects such proposals, stating:

EWG found various problems with the proposed approach, and didn't think it's feasible to try solving the problem, as it has been tried many times and every time it has failed

Ben Voigt's comments have helped me to see the insurmountable problems with this approach; given:

struct X {
    int c;
    char a;
    float b;
};

What order would these functions be called in in : struct X foo = {.a = (char)f(), .b = g(), .c = h()}? Surprisingly, in :

The order of evaluation of the subexpressions in any initializer is indeterminately sequenced [1]

(Visual C++, gcc, and Clang seem to have an agreed upon behavior as they will all make the calls in this order:)

  1. h()
  2. f()
  3. g()

But the indeterminate nature of the standard means that if these functions had any interaction the resulting program state would also be indeterminate, and the compiler wouldn't warn you: Is there a Way to Get Warned about Misbehaving Designated Initializers?

does have stringent initializer-list requirements 11.6.4[dcl.init.list]4:

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (17.5.3), 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.

So support would have required this to be executed in the order:

  1. f()
  2. g()
  3. h()

Breaking compatibility with previous implementations.
As discussed above, this issue has been circumvented by the limitations on Designated Initializers accepted into . They provide a standardized behavior, guaranteeing the execution order of Designated Initializers.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    Except it is not "order-independent" at all, the proposal evaluates initializer expressions in the lexical order of declaration of the members, not the order in which the initializers are designated. Talk about a design that causes surprising behavior! – Ben Voigt Mar 30 '15 at 03:32
  • @BenVoigt Interesting, I hadn't read that in full when I posted the link. I'm not sure I understand how this would create a problem though, as I believe list initialization is illegal unless the object does not have a constructor, or has a constructor specifically written to take an `initializer_list`. In either case the first case the initialization order should make no difference, in the second case initialization is constructor controlled anyway, so I don't see a problem there either. Clearly you understand something I don't though, so could you elaborate further on, "surprising behavior"? – Jonathan Mee Mar 30 '15 at 03:41
  • 3
    Sure, in this code: `struct X { int c; char a; float b; }; X x = { .a = f(), .b = g(), .c = h() };` the call to `h()` is performed before either `f()` or `g()`. If the definition of `struct X` is not nearby, this is going to be very surprising. Remember that initializer expressions don't have to be side-effect free. – Ben Voigt Mar 30 '15 at 03:43
  • 2
    Of course, this is nothing new, ctor member initialization already has this issue, but it's in the definition of a class member, so tight coupling is no surprise. And designated initializers can't reference the other members the way the ctor member-initializers can. – Ben Voigt Mar 30 '15 at 03:46
  • @BenVoigt it'd hardly be the only surprise in C++ initialization. It doesn't seem any more extreme than the order of execution of items in the ctor-initializer list not being executed in the order they appear. – M.M Mar 30 '15 at 03:50
  • 2
    @MattMcNabb: No, it's not more extreme. But one expects the developer implementing the class constructor to know the member declaration order. Whereas the consumer of the class might be a different programmer entirely. Since the whole point is to allow initialization without having to look up the order of members, this seems like a fatal flaw in the proposal. Since designated initializers can't reference the object being constructed, first impression is that initialization expressions could be evaluated first, in designation order, then member initialization in declaration order. But... – Ben Voigt Mar 30 '15 at 03:53
  • aliasing between parameters passed to the various member constructors potentially still causes problems. It just means that `X x{ y, std::move(y) };` is safe for a true aggregate, but not with a user-defined constructor, and not if designated initializers are brought into the mix. – Ben Voigt Mar 30 '15 at 03:56
  • @BenVoigt as with things like `i = ++i;` the answer will be "technically case X is OK and case Y is UB, but Don't Do That." – M.M Mar 30 '15 at 03:58
  • @BenVoigt It seems that by virtue of the fact that the "consumer of the class" is directly naming and using the members that he must also be privy to the implementation order. – Jonathan Mee Mar 30 '15 at 03:59
  • Which makes (part of) the alleged rationale for the feature a lie. – Ben Voigt Mar 30 '15 at 04:06
  • @BenVoigt: the initialisation order issue only matters when members are referencing each other. Why not just make the compiler emit a warning (or even an error) in this particular case. This leave open many use cases. Anyway if the initialisation order matters it *has to be documented in the structure API* because the user is not supposed to have any other access to structure definition (well, textual headers... but hopefully they will disappear someday). – kriss Jan 05 '16 at 13:45
  • 1
    @kriss While I agree with your fervor to vindicate designated intialization, there is a problem beyond members referencing each other. In fact, [it's an active problem in C99](http://stackoverflow.com/questions/34614308/is-there-a-way-to-get-warned-about-misbehaving-designated-initializers). I've updated my answer to help clarify this. In any case, I do agree with your thought. If the designers said this was an acceptable risk in C99, why not in C++99 too? – Jonathan Mee Jan 05 '16 at 14:51
  • 1
    @Jonathan Mee: Yes, this is indeed an issue. The same problem also exists in C++ (for the same reason) when calling member initializers. Not worse than UB in expressions (this problem could be left as UB for initializer, why bother to define order?) It wouldn't worry me too much. The main use case I see for new C style initializers are initializers of library provided structures (static or not). The designated initialisers are the nearest thing I know of in C or C++ of python style named parameters (instead of order based ones). – kriss Jan 05 '16 at 16:38
  • 2
    @JonathanMee: Well, the other question answered that... C99 aggregate initializers are unordered, so there's no expectation for designated initializers to be ordered. C++ braced-init-lists ARE ordered, and the proposal for designated initializers uses a potentially-surprising order (you can't be consistent both with lexical order, used for all braced-init lists, and member order, used for ctor-initializer-lists) – Ben Voigt Jan 06 '16 at 18:33
  • @BenVoigt Thanks for sticking with me while I work through this. I have updated the answer with a new-found understanding of what I believe you were communicating. I'd appreciate if you could certify this edit. – Jonathan Mee Jan 06 '16 at 20:25
  • But who cares about order? If you need to enforce some order, you use auxiliary variables for this, like to solve sequence point problems. Ehh why did it have to be rejected. – rr- Jan 08 '16 at 16:14
  • @rr- Was this proposal rejected? – Jonathan Mee Jan 08 '16 at 16:17
  • ...right, I confused it with N4172. This one didn't even get a document number. – rr- Jan 08 '16 at 18:32
  • 1
    @rr- Yeah that's what I was going to say I haven't seen a reference number for this one. Does that mean anything? I guess if it's almost two years old it means it's lost to time :( I'd still like to see a rationale for not accepting it. (Even though the rationale would probably be pretty similar to what I have in my answer.) – Jonathan Mee Jan 08 '16 at 19:35
  • 7
    Jonathan: "c++ support would have required this to be executed in the order [...] Breaking compatibility with previous c99 implementations." I don't get this one, sorry. 1. If the order is indeterminate in C99, then obviously *any* actual order should be fine, including any arbitrary C++ choice. b) Not supporting the des. initializers at all kinda already breaks C99 compatibility even more... – Sz. Feb 05 '18 at 20:37
  • 1
    @Sz. C++ doesn't draw from the C99 standard. So there isn't compatibility there to start with. The comment is intended as, "If you take code that was compiled against C99, and tried to implement designated initializer lists as C++ would require, the behavior would differ." – Jonathan Mee Feb 05 '18 at 21:02
  • Ah, OK then, thanks. (I know they are unrelated, I just tried to make the most sense of your compatibility note.) Anyhow, designated initializers are likely just a matter of time for C++ to finally pick up. (Alas, probably too much time, as [it has become the norm with the evolution of the language](https://www.reddit.com/r/cpp/comments/49dgdb/why_i_am_not_happy_with_c17_c_17_outlook_march/).) – Sz. Feb 24 '18 at 23:54
  • 2
    @Sz. There was a lot more I could have dreamed of in C++17, this feature included... but I'm just so grateful the language is still growing I struggle to complain overall. There was just a lot of time before C++11, where the language I loved seemed to be dying. At this point... yeah, I'm just grateful. – Jonathan Mee Feb 26 '18 at 00:16
  • A simple restriction of implementation-defined edge case behavior (from another language which is already incompatible!) is what the C++ community considers to be "insurmountable"? No wonder the language is such a mess. – Stuntddude Nov 13 '18 at 20:45
  • @Stuntddude There are 2 schools of thought on this, right? On one hand backwards comparability is valuable. On the other the evolution of the language to apply to today's problems is valuable. One of the things that has made C++ a successful standard is that it has navigated the intersections of these 2 goods with great care. In any case it sounds like we'd both agree that the standard committee has made the right decision here. – Jonathan Mee Nov 13 '18 at 21:54
  • 1
    The right decision? They waited 20 years and then introduced a version of designated initialization so crippled it's nearly useless, largely killing the hope of proper designated initializers making it into the language. Now it's just another burden on compiler authors rather than something to get excited about. – Stuntddude Nov 15 '18 at 22:37
  • @Stuntddude I was actually really excited about this :/ I felt it gave me more than enough tools to do what I wanted, and on top of that it wasn't implementation defined, meaning that I have guaranteed behavior on any compliant compiler. All the shortcomings I could think of had simple workarounds, perhaps you have an example of a shortcoming that lacks a simple workaround? – Jonathan Mee Nov 16 '18 at 14:48
  • @JonathanMee Maybe [errnoname](https://github.com/mentalisttraceur/errnoname) (the array-based optimization variant, not the `switch`-based default variant) is an example of something hard to do without out-of-order designated initializers for arrays? Or does that have a simple workaround too? (If so I'll post a separate question.) – mtraceur Dec 30 '19 at 12:40
  • @JonathanMee Actually a link to the whole repo might be confusing - I mean [the array-building code that starts here](https://github.com/mentalisttraceur/errnoname/blob/master/errnoname.c#L23). Note the fact that there are no guarantees available about numerical value, not even relative ordering thereof, and the values are not available until compile (pre-processing) time. – mtraceur Dec 30 '19 at 12:49
  • Re: "On July 15 '17 P0329R4 was accepted into the c++20 standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf": why having two same links (link to P0329R4 is the same)? It seems that "was accepted into the c++20 standard" needs to be followed by the _link to the standard_. – pmor Nov 25 '22 at 14:49
47

A bit of hackery, so just sharing for fun.

#define with(T, ...)\
    ([&]{ T ${}; __VA_ARGS__; return $; }())

And use it like:

MyFunction(with(Params,
    $.Name = "Foo Bar",
    $.Age  = 18
));

which expands to:

MyFunction(([&] {
 Params ${};
 $.Name = "Foo Bar", $.Age = 18;
 return $;
}()));
TankorSmash
  • 12,186
  • 6
  • 68
  • 106
keebus
  • 990
  • 1
  • 8
  • 15
42

C++ has constructors. If it makes sense to initialize just one member then that can be expressed in the program by implementing an appropriate constructor. This is the sort of abstraction C++ promotes.

On the other hand the designated initializers feature is more about exposing and making members easy to access directly in client code. This leads to things like having a person of age 18 (years?) but with height and weight of zero.


In other words, designated initializers support a programming style where internals are exposed, and the client is given flexibility to decide how they want to use the type.

C++ is more interested in putting the flexibility on the side of the designer of a type instead, so designers can make it easy to use a type correctly and difficult to use incorrectly. Putting the designer in control of how a type can be initialized is part of this: the designer determines constructors, in-class initializers, etc.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • 12
    Please show a reference link for what you say is the reason for C++ to not have designated initializers. I can't remember having ever seen the proposal for it. – Johannes Schaub - litb Sep 11 '13 at 19:38
  • 26
    Isn't the very reason of not providing a constructor for `Person` that its author wanted to provide the most possible flexibility for users to set and initialize the members? The user can also already write `Person p = { 0, 0, 18 };` (and for good reasons). – Johannes Schaub - litb Sep 11 '13 at 19:48
  • 7
    Something similar has recently been accepted into the C++14 spec by http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3605.html . – Johannes Schaub - litb Sep 11 '13 at 19:49
  • 4
    @JohannesSchaub-litb I'm not talking about the purely mechanical, proximate cause (i.e., it hasn't been proposed to the committee). I'm describing what I believe to be the dominating factor. — `Person` has a very C design so C features may make sense. However C++ probably enables a better design which also obviates the need for designated initializers. — In my view removing the restriction on in-class initializers for aggregates is much more in line with the ethos of C++ than designated initializers. – bames53 Sep 11 '13 at 20:42
  • @JohannesSchaub-litb Then i ask you if weight covered with ifdef block and ignored. you have to know how many fields before age. And dont ever say it makes no sense; look linux kernel code, there are gazillion fields like that. – Abdurrahim Nov 09 '14 at 17:01
  • 4
    The C++ replacement for this could be named function arguments. But as of right now, name arguments don't officially exist. See [N4172 Named arguments](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4172.htm) for a proposal of this. It would make code less error prone and easier to read. – David Baird Nov 29 '15 at 15:11
  • 1
    Boost already provides named function parameters. IMHO, it's sad that C++ doesn't have this feature like C. – hedayat Mar 09 '16 at 10:20
  • @DavidBaird I presume what you meant was that, with named arguments, one could declare a constructor with defaults for all arguments, then let the user choose which to override, via names. Is that right? – underscore_d Apr 19 '16 at 17:01
  • If we shouldn't have designated initializers so that we can put the designer of the type in control, we shouldn't have plain structs (without constructors), either, and only allow classes. – musiphil Jan 24 '17 at 19:22
  • 1
    -1: C# has constructors and has designated initializer like initialization for struct/class. It's syntactic sugar and would be safe to add. Only reason for C++ not having this: someone voted against it in the committee – ceztko Jun 08 '17 at 23:15
  • C#'s design motivations are different in a lot of ways from C++, so I don't see what that would have to do with this question. And again, the committee rejecting this change is merely the proximate cause. What's important is why the committee rejected it. It didn't reject it because it mistakenly thought the change would be "dangerous", so the fact that this feature is "safe" is irrelevant. – bames53 Jun 09 '17 at 19:11
  • Designated initializers make initializing a large const struct much cleaner. This comes up, for example, if your class has an instance of a configuration struct, and also static const instances of the struct that define the maximum and minimum allowed configuration values. If the config is a list of a dozen floats and a few ints, the constructor gets messy. Splitting it into multiple sub-objects is not always the right abstraction. You can work around this with const_cast and storing a const pointer to the struct, but why should C++ make this harder than it is in C? – odougs Mar 09 '19 at 06:03
  • 1
    Designated initializers are coming in [C++20](https://en.cppreference.com/w/cpp/language/aggregate_initialization) – ceztko Sep 18 '19 at 08:26
  • So they needed 21 years to **re**introduce a featue that was there before (my g++ supports it anyway, but VS complains) and even dare to still require us to provide fields in the declaration order? Btw. *we do not want, because internals are exposed* is an invalid argument, because having to look up the declaration order of fields is a heavier internals exposure (and also error-prone in case of insertions of new fields) than just throwing in some name-value pairs. Looks to me if there are people in the commitee that shouldn't be there. – Neonit Nov 19 '19 at 07:42
  • 1
    @Neonit Designated initializers are supported in C since C99, C++ branched from C before that standard, so it is not **re**introduction. That feature has never been part of C++ (prior C++20), nor was it removed from the "C/C++ branching point" (hopefully that makes sense) – Maliafo Jan 28 '20 at 10:20
24

Designated initializer are currently included in C++20 body of work: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf so we might finally see them!

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 4
    But do note that they are restricted: _In C++, designated initialization support is restricted compared to the corresponding functionality in C. In C++, designators for non-static data members must be specified in declaration order, designators for array elements and nested designators are not supported, and designated and non-designated initializers cannot be mixed in the same initializer list._ This means that in particular, you still won't be able to [easily make a an enum-keyed lookup table](http://eli.thegreenplace.net/2011/02/15/array-initialization-with-enum-indices-in-c-but-not-c). – Ruslan Aug 29 '17 at 10:43
  • @Ruslan: I wonder why C++ restricted them so much? I understand that there could be confusion about whether the order in which items's values are evaluated and/or written to the struct matches the order in which items are specified in the initalization list, or the order in which members appear in the struct, but the solution to that would simply be to say that initialization expressions are executed in arbitrary sequence, and the lifetime of the object does not begin until initialization is complete (the `&` operator would return the address that the object *will* have during its lifetime). – supercat Aug 16 '18 at 20:51
7

Two Core C99 Features that C++11 Lacks mentions “Designated Initializers and C++”.

I think the ‘designated initializer’ related with potential optimization. Here I use “gcc/g++” 5.1 as an example.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>    
struct point {
    int x;
    int y;
};
const struct point a_point = {.x = 0, .y = 0};
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

We knew at compilation time, a_point.x is zero, so we could expected that foo is optimized into a single printf.

$ gcc -O3 a.c
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004f0 <+0>: sub    $0x8,%rsp
   0x00000000004004f4 <+4>: mov    $0x4005bc,%edi
   0x00000000004004f9 <+9>: xor    %eax,%eax
   0x00000000004004fb <+11>:    callq  0x4003a0 <printf@plt>
   0x0000000000400500 <+16>:    xor    %eax,%eax
   0x0000000000400502 <+18>:    add    $0x8,%rsp
   0x0000000000400506 <+22>:    retq   
End of assembler dump.
(gdb) x /s 0x4005bc
0x4005bc:   "x == 0"

foo is optimized to print x == 0 only.

For C++ version,

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct point {
    point(int _x,int _y):x(_x),y(_y){}
    int x;
    int y;
};
const struct point a_point(0,0);
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

And this is output of the optimized assemble code.

g++ -O3 a.cc
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function _Z3foov:
0x00000000004005c0 <+0>:    push   %rbx
0x00000000004005c1 <+1>:    mov    0x200489(%rip),%ebx        # 0x600a50 <_ZL7a_point>
0x00000000004005c7 <+7>:    test   %ebx,%ebx
0x00000000004005c9 <+9>:    je     0x4005e0 <_Z3foov+32>
0x00000000004005cb <+11>:   mov    $0x1,%ebx
0x00000000004005d0 <+16>:   mov    $0x4006a3,%edi
0x00000000004005d5 <+21>:   xor    %eax,%eax
0x00000000004005d7 <+23>:   callq  0x400460 <printf@plt>
0x00000000004005dc <+28>:   mov    %ebx,%eax
0x00000000004005de <+30>:   pop    %rbx
0x00000000004005df <+31>:   retq   
0x00000000004005e0 <+32>:   mov    $0x40069c,%edi
0x00000000004005e5 <+37>:   xor    %eax,%eax
0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
0x00000000004005ec <+44>:   mov    %ebx,%eax
0x00000000004005ee <+46>:   pop    %rbx
0x00000000004005ef <+47>:   retq   

We can see that a_point is not really a compile time constant value.

Austin Adams
  • 6,535
  • 3
  • 23
  • 27
wcy
  • 875
  • 11
  • 12
  • 10
    Now please try `constexpr point(int _x,int _y):x(_x),y(_y){}`. clang++'s optimizer seems to eliminate the comparison in your code as well. So, this is just a QoI issue. – dyp May 25 '15 at 11:13
  • I would also expect the entire a_point object to be optimized away if it had internal linkage. i.e. put it in the anonymous namespace and see what happens. http://goo.gl/wNL0HC – Arvid Jul 10 '15 at 00:51
  • @dyp: Even just defining a constructor is possible only if the type is under your control. You cannot do that, for example, for `struct addrinfo` or `struct sockaddr_in`, so you're left with assignments separate from declarations. – musiphil Jan 24 '17 at 19:27
  • 2
    @musiphil At least in C++14, those C-style structs can be properly set up in a constexpr function as local variables by using assignment, and then returned from that function. Additionally, my point was not to show an alternative implementation of the constructor in C++ which allows the optimization, but showcase that it is possible for the compiler to perform this optimization if the form of initialization is different. If the compiler is "good enough" (i.e. supports this form of optimization), then it should be irrelevant whether you use a ctor or designated initializers, or something else. – dyp Jan 25 '17 at 08:38