Why does "ctor() = default" change behavior when other constructors are present?
Asked Answered
S

2

11

Why does

struct wrapper
{
    explicit wrapper(void *);
    wrapper() = default;
    int v;
};

int main() { return wrapper().v; }  // You should run this in Debug mode

return 0xCCCCCCCC, whereas

struct wrapper { wrapper() = default; int v; };
int main() { return wrapper().v; }

and

struct wrapper { int v; };
int main() { return wrapper().v; }

both return 0?

Sams answered 24/12, 2014 at 22:59 Comment(13)
when you add the non-=default constructor, the type is no longer an aggregate. I'm sure that's playing into this, not sure how though. I doubt you're guaranteed that it's 0 in either case.Carbrey
@RyanHaining: Are you sure? If so then it seems weird because it would imply the only time you could use = default would be when there are no other constructors present, which seems pointless...Sams
The =default is going to give back the default constructor, but v is unintialized in both cases. With gcc I get 0 even in the first case, but it's all just by chance.Carbrey
@RyanHaining: Do you still get zero in the first case with GCC if you explicitly declare the constructor as wrapper() { } (i.e. avoid initializing v)? (Don't forget to do both of these experiments in debug mode with no optimizations.)Sams
in that case I don't, no, I get a huge value as well. I think I'm mistaken about the 0 guarantees actually. I'm looking at the rules atmCarbrey
@RyanHaining: Yeah I think so too :) I'm pretty sure = default gives you the same default constructor that would've been implicitly declared if the user hadn't declared any constructors, but feel free to correct me if I'm wrong.Sams
Let us continue this discussion in chat.Carbrey
What standard, if any, is VS-2013 supposed to implement? This kind of thing might change subtly between c++03, c++11 and c++14.Weismann
@juanchopanza: C++11 with some aspects of C++14: blogs.msdn.com/b/vcblog/archive/2013/11/18/… Although I don't see why it would change from C++11 to C++14...Sams
Maybe it's this bug?Branum
(For the rest of you, this question is very related to https://mcmap.net/q/1158268/-how-to-make-a-user-defined-type-initialize-exactly-like-a-built-in-type/541686)Sams
@dyp: Looks related but I can't tell if it's the same.Sams
It seems that deleting the converting constructor makes VS behave reasonably again. I suspect it might have to do with the part about "no user-provided constructors" in the value-initialization specification. Anyway, I'd say it's a bug.Branum
S
4

During value-initialization, if T is a class type without a user-provided or deleted default-constructor, then the object is zero-initialized (§8.5/8.2). This is indeed the case with wrapper.

Your first example matches the third case for zero-initialization (§8.5/6.1, emphasis mine)

— if T is a scalar type (3.9), the object is initialized to the value obtained by converting the integer literal 0 (zero) to T;

if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class sub-object is zero-initialized and padding is initialized to zero bits;

— if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero-initialized and padding is initialized to zero bits;

— if T is an array type, each element is zero-initialized

— if T is a reference type, no initialization is performed

So in your first example, v should be zero-initialized. This looks like a bug.

In your second and third example you no longer have a user-provided constructor, but you do have a default-constructor that isn't user-provided or deleted so your example still falls into the third case for zero-initialization, which is to zero-initialize each non-static data member. VS is correct there.

Stumer answered 24/12, 2014 at 23:49 Comment(7)
There's been a defect in the C++11 Standard. If you read the IS, it says "without a user-provided constructor", which lacks the crucial "default".Branum
@Mehrdad :D oh, that has become ambiguous recently - I'm referring to the International Standard. See also: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1301Branum
@Branum Would the redaction affect the answer?Stumer
@dyp: LOL if meant what I think you meant that was pretty funny. And I see haha, thanks for the link.Sams
@0x499602D2 I'm not entirely sure what you're referring to. A language-lawyer could argue that VS is behaving perfectly conforming, since the first wrapper does have a user-provided constructor (the converting ctor). -- edit: hm, user-provided is only defined for special member functions..Branum
This exact behavior is noted in the code snippet in CWG 1368, resolved in the same change @Branum linked.Acetanilide
@Acetanilide What I don't quite understand is how the C++11 text mandates this behaviour. Does user-provided constructor also match the non-default ctor? It's not a special member function..Branum
C
4

This does appear to be a bug in MSVC. In all three cases wrapper has no user-provided default constructor, so initialization with wrapper() invokes:

(All citations from n3690)

(8.5/11) An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

(thanks to dyp), this will result in zero-intialization of int v

Initialization then refers us to the rule:

(8.5/8) if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked.

The zero initialization rules state:

(8.5/6) if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits

int v being a data member of wrapper is zero initialiazed itself according to:

(8.5/6) if T is a scalar type (3.9), the object is initialized to the value obtained by converting the integer literal 0 (zero) to T

Which is not the behavior you observe.

Carbrey answered 25/12, 2014 at 0:0 Comment(3)
It'd be nice if you could explain why GCC and Clang are going through the extra trouble to value-initialize everything if you think VC++ is correct in not doing so.Sams
@Mehrdad I reversed everythingCarbrey
@Branum this is the subtlest c++ point I've ever (been pushed to) hit.Carbrey

© 2022 - 2024 — McMap. All rights reserved.