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?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    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. – Ryan Haining Dec 24 '14 at 23:01
  • @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... – user541686 Dec 24 '14 at 23:02
  • 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. – Ryan Haining Dec 24 '14 at 23:06
  • @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.) – user541686 Dec 24 '14 at 23:08
  • 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 atm – Ryan Haining Dec 24 '14 at 23:10
  • @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. – user541686 Dec 24 '14 at 23:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67646/discussion-between-ryan-haining-and-mehrdad). – Ryan Haining Dec 24 '14 at 23:12
  • 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. – juanchopanza Dec 24 '14 at 23:24
  • @juanchopanza: C++11 with some aspects of C++14: http://blogs.msdn.com/b/vcblog/archive/2013/11/18/announcing-the-visual-c-compiler-november-2013-ctp.aspx Although I don't see why it would change from C++11 to C++14... – user541686 Dec 24 '14 at 23:27
  • Maybe it's [this bug](https://connect.microsoft.com/VisualStudio/Feedback/Details/917875)? – dyp Dec 24 '14 at 23:45
  • (For the rest of you, this question is very related to http://stackoverflow.com/q/22265255/541686) – user541686 Dec 24 '14 at 23:51
  • @dyp: Looks related but I can't tell if it's the same. – user541686 Dec 24 '14 at 23:53
  • 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. – dyp Dec 25 '14 at 00:11

2 Answers2

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.

David G
  • 94,763
  • 41
  • 167
  • 253
  • 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". – dyp Dec 24 '14 at 23:50
  • 6
    @Mehrdad :D oh, that has become ambiguous recently - I'm referring to the International Standard. See also: http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1301 – dyp Dec 24 '14 at 23:52
  • @dyp Would the redaction affect the answer? – David G Dec 24 '14 at 23:53
  • @dyp: LOL if meant what I think you meant that was pretty funny. And I see haha, thanks for the link. – user541686 Dec 24 '14 at 23:55
  • @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.. – dyp Dec 24 '14 at 23:56
  • This exact behavior is noted in the code snippet in [CWG 1368](http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1368), resolved in the same change @dyp linked. – T.C. Dec 25 '14 at 02:56
  • @T.C. 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*.. – dyp Dec 25 '14 at 20:12
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.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174