5 ms·
> Force the programmer to write what they meant, sometimes they meant zero, or None, or 0.0 or whatever, but surprisingly often when confronted with the questio
by tsimionescu 3y ago
> Force the programmer to write what they meant, sometimes they meant zero, or None, or 0.0 or whatever, but surprisingly often when confronted with the question the programmer realises their design is wrong and needs a design level change.
I almost sympathize with your point, except we are taking about a language where `Type var;` is the explicit way to initialize many variables to a perfectly well defined value: it is the only way to call the no-arguments constructor for a variable on the stack. It's only for non-class types that this has the bizarre behavior of allocating but not setting any value.
It's even worse in a language with templates:
template <class T>
void foo() T {
T local;
return local;
}
Can be perfectly correct OR it can be UB based on the type of T.
- tialaramex 3y ago> it is the only way to call the no-arguments constructor for a variable on the stack. Is that really true? Ouch. In many languages that wouldn't feel crazy, but in a language where there's a whole book about initialization https://leanpub.com/cppinitbook https://leanpub.com/cppinitbook that feels kinda silly.
- tsimionescu 3y agoA sibling response pointed out that adding an empty pair of braces (an ampty initializer list) after the var name can also invoke the no-args constructor, but it can also do other things depending on the class. So yes, I believe this is the only way of explicitly calling the no-args constructor whole in-place on a stack variable. Ideally the syntax `T var();` would have worked as well, but it turns out that it would be ambiguous with declaring a local function named var that takes no arguments and returns a T...
- gpderetta 3y ago> [...] it is the only way to call the no-arguments constructor for a variable on the stack The syntax: T var{}; always value-initializes a stack allocated variable (or a member variable or a global).
- tsimionescu 3y agoI didn't know about the empty initializer list syntax. Still, reading about it, there are cases where `T var{} ;` will do something different from `T var;`: if T is an aggregate object type, then it will invoke aggregate initialization instead of calling the no-args constructor.
- gpderetta 3y agoIf T is an aggregate, there is, by definition, no constructor (no-argument or otherwise) to call. The aggregate initialization will then recursively value initialize each member, which is what you want. The only catch is, as usual, list-initialiation. You have to hope that T is sane and any list initialization constructor with an empty list is equivalent to the nullary constructor.
- tsimionescu 3y agoThere was one case I found on SO and later reproduced, where a class B derives publically from another class A which has a protected no-args constructor. In that case, B b; is valid, but B b{}; is not, since it tries to construct an instance of A from the calling code itself using the protected constructor, which it's obviously not allowed to access. Overall I think it's safe to say that the two syntaxes have different semantics, even if they overlap in most cases.
- gpderetta 3y agoI have to try that. There are defect reports for corner cases. In any case this is also allowed: T val = T(); And copy elision is now guaranteed.
- tsimionescu 3y agoThe example looks like this: class A { protected: A() {} } class B : public A {} B x; //ok B y{}; //not ok, can't access A::A() Would the T val = T(); example work if you don't have a copy constructor at all, or no move constructor, or custom ones which do weird things? Edit: I checked, and you're right - the syntax seems to be fully equivalent in C++17 or later. Great to hear!