C++ tidbit #10: `?=default`? Impact on Initialization

C++ tidbit #10: `=default` Impact on Initialization

Here's just one gotcha of dozens lurking within C++ initializations.

The following is called 'value initialization':

int a = int();        

It is defined as a type name followed by a pair of empty parens. It means: if the type has a user provided default ctor, call it. Otherwise, perform 'zero-initialization'.

So when the user does not provide a default ctor:


?
struct Thingy1 {  int i; };
Thingy1 t1;
cout << t1.i << endl;          // Junk
cout << Thingy1().i << endl; ? // Zero        

The compiler-generated ctor does nothing, leaving in `i` whatever bits happened to be on the stack at the moment. Thingy1() is not a ctor call - it is value initialization, zeroing `i`.

When the user does provide a default ctor:


struct Thingy2 {
  Thingy2() {};
  int i;
};
cout << Thingy2().i << endl; ? // Junk        

Thingy2() now means a ctor call - and like the compiler-generated one, this particular empty ctor doesn't touch `i`.

So far so good. 'Zero initialization' actually does mostly what you think it does - the trap lies elsewhere. As you have no doubt meticulously studied previous articles in this series, you're surely alerted by the terminology 'user provided'.



struct Thingy3 {
? Thingy3() = default; // user DECLARED, not provided!
? int i;
};
cout << Thingy3().i << endl; ? // Zero again!        

It is entirely reasonable to expect a special member function marked as `=default` would behave the same as a compiler-generated one. However, C++ chose (knowingly? not sure) to treat functions marked `=default` as user declared, but not provided. Thingy3() doesn't mean a ctor call (because it isn't user provided) but rather zero initialization.

No alt text provided for this image

Go ahead and play with it yourself .


It has been known for a while that C++ initialization is bonkers . Seriously bonkers . Timur Doumler put it best:

No alt text provided for this image

What worries me is that it actually keeps getting worse. Here's another Timur talk, from C++Now2022. He presents an initialization method added by C++20 which he calls 'direct aggregate initialization', spends 20 seconds describing the problem it solves and 4.5 minutes the various pitfalls it introduces and devious little differences from other initializations.


Have you heard about Microsoft's minus-100? Here are some snippets from a nearly 20yo blog by one of C# language designers :

We worked hard to keep the complexity down. One way to do that is through the concept of “minus 100 points”. Every feature starts out in the hole by 100 points, which means that it has to have a significant net positive effect on the overall package for it to make it into the language.
...
Yes, being able to [..whatever..] is somewhat more convenient than having to write the test yourself, but it doesn't really enable you to do anything new.
...
Some features provide enough utility in the abstract, but when we go to come up with a workable design for the feature, we find that we can't come up with a way to make it simple and understandable for the user.

I don't think `direct aggregate initialization` would have survived a minus-100 filter. I doubt half of C++'s initialization methods would.

It is understandably tempting to add a feature that solves a small problem because the benefit is entirely tangible and the cost isn't - but the added complexity in interaction with the myriad previous features is there even if you can't see it during design. I guess it takes some humility and maybe courage to consider such unknowns, not entirely sure I'd have coped better myself.

Alex Dathskovsky ?

Director of Software Engineering @ Speedata.io | C++ Guru and Speaker | ISO C++ standardization group member

1 年

I really agree with this, the number of ways to initialize things in C++ is horrific and the pitfalls that they introduce in each and every one. I hope it would be possible to deprecate many of those but I guess it won't be possible for backwards compatibility.

要查看或添加评论,请登录

Ofek Shilon的更多文章

  • Compiler Limitations #3/3

    Compiler Limitations #3/3

    Some examples, before the main point As discussed in the 1st post in this series, clang isn't able to properly express…

    3 条评论
  • C++ tidbit #9: nullptr_t doesn't behave like a pointer type?

    C++ tidbit #9: nullptr_t doesn't behave like a pointer type?

    Suppose you want to customize some behavior for compile-time nullptrs. As a concrete example let's take a comparison of…

    11 条评论
  • C++ tidbit #8: Damaging Default Destructor

    C++ tidbit #8: Damaging Default Destructor

    Special member functions are implicitly generated by the compiler if the user didn't provide them - constructors…

    9 条评论
  • C++ tidbit #7: Lifetime Extension by Const Ref

    C++ tidbit #7: Lifetime Extension by Const Ref

    Const-ref can bind to a temporary..

    11 条评论
  • C++ tidbit #6: Virtual functions and trivial copy

    C++ tidbit #6: Virtual functions and trivial copy

    Trivial Copyability In C++ terms an object is `trivially-copyable` if it is ok to memcpy it around. Containers (stl and…

  • C++ tidbit #5: const static members

    C++ tidbit #5: const static members

    struct S { static const int x = 0; }; int? n = S::x; const int m = S::x; -- link error: undefined reference to…

    2 条评论
  • C++ tidbit #4: Signed UB

    C++ tidbit #4: Signed UB

    All happy integers are alike, but every unhappy integer is unhappy in its own way Since the early computing days all…

    7 条评论
  • C++ tidbit #3: Contextual Conversion

    C++ tidbit #3: Contextual Conversion

    Warmup: explicit constructors When you mark a cast operator as explicit: struct C { explicit operator bool() {…

    1 条评论

社区洞察

其他会员也浏览了