2

Given two classes with different constructors:

#include <iostream>

struct A {
    int x;

    A() {};
};

struct B {
    int x;

    B() = default;
};

int main() {
    int x = 5;
    x = 7;

    printf("before: %d\n", x);

    new(&x) A();
    printf("%d\n", x);

    new(&x) B();
    printf("%d\n", x);
}

Output is:

before: 7
7
0

Why default ctor zero-initializes int x?

vladon
  • 8,158
  • 2
  • 47
  • 91
  • @mediocrevegetable1 there is no `{}`-initialization, is `= default` for ctor implicitly {}-init every time? even if I specify `()`? – vladon Nov 01 '21 at 15:12
  • 1
    This is an interesting result, but due to the nature of Undefined Behavior, this doesn't show that `B` actually initializes its `x` member. For example when I tried it I got the same result as you, but with `-O3` I get an output that looks like `A` also initializes `x` to zero, but it obviously isn't required to : https://godbolt.org/z/cxrn1WcT9 – François Andrieux Nov 01 '21 at 15:13
  • @vladon ignore my last comment actually. I'm not entirely sure, so what I'm saying may be false. – mediocrevegetable1 Nov 01 '21 at 15:15
  • `Why default ctor zero-initializes int x` first off all who said it does? – Slava Nov 01 '21 at 15:15
  • 1
    I think it is not the constructor who did that but it is the integer initialization and it is undefined and compiler dependant – Anis Belaid Nov 01 '21 at 15:17
  • @AnisBelaid so the question is: Is this undefined behavior? – vladon Nov 01 '21 at 15:21
  • @Slava Ok, is zero-initializing defined by standard or is it undefined behavior? – vladon Nov 01 '21 at 15:22
  • 2
    @vladon Since C++11 (I think), if you call new() for a class with a "defaulted default ctor" (your class B has one) ["the object is zero-initialized"](https://en.cppreference.com/w/cpp/language/value_initialization) as one possibility of value initialization. Case 2 on that page. It would then be default-initialized which is a NOP for A and B. – Peter - Reinstate Monica Nov 01 '21 at 15:24
  • @FrançoisAndrieux Is it kosher to construct a different type in the space occupied by an int? (It would surely be OK with a char array.) Does it matter that the actual object in that class is actually an int, so that an int is replacing an int? (Because you can legitimately cast the pointer to an aggregate to a pointer to its first member.) – Peter - Reinstate Monica Nov 01 '21 at 15:28
  • 1
    @Peter-ReinstateMonica yes, but not on Saturdays – vladon Nov 01 '21 at 15:30
  • @Peter-ReinstateMonica I believe it is allowed provided size and alignment are compatible, but reusing `x` afterwards as an `int` wouldn't be (such as in this example's `printf`). The `printf` would need to print the new object's member instead. – François Andrieux Nov 01 '21 at 15:31
  • 1
    @vladon As everybody knows, StackOverflow is not work -- it's *avoiding* work, so it's best done on Sabbath. – Peter - Reinstate Monica Nov 01 '21 at 15:32
  • Some good viewing: [The Nightmare of Initialization in C++](https://www.youtube.com/watch?v=7DTlWPgX6zs) – user4581301 Nov 01 '21 at 15:53

1 Answers1

6

You used value initialization (new A();) which is different from default initialization (new A;). Notice the parenthesis.

For value initialization :

if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;

And :

if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;

And, on the definition of "user-provided" :

A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

A has a user provided constructor so it falls in the first case. The just calls its constructor which initializes nothing.

B 's constructor is explicitly defaulted so it isn't user provided, and it is also not deleted, so it falls into the second case. It is zero-initialized then default initializated.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • 1
    I must admit that these arcane rules are confusing. The possibility to have "defaulted default constructors" was essentially introduced to save the programmer from writing code that the compiler knew very well how to generate already (and that it would maintain automatically as well!). Now although I explicitly say "do what you always do" it does something different?? Beats me. That said, zero-initialization should have been the default instead of "do nothing and let UB eat you" since 1989, with the possibility to opt-out in the 0.1% where performance mandates it. – Peter - Reinstate Monica Nov 01 '21 at 15:35
  • @Peter-ReinstateMonica The trick here is that `B() = default` leaves `B` as a trivial type while `A() {}` doesn't. If you compare `A a{}; B b{};` you would get the same situation. Basically `= default` doesn't mean "write an empty constructor". It means "don't let anything get in the way of generating a compiler-provided default constructor" which is subtly different. In a sense it "does what you always do" because `B` is equivalent to if `B() = default;` hadn't been written at all. Edit : I agree zero-initialization of uninitialized members should be the default, with a way to opt-out. – François Andrieux Nov 01 '21 at 15:41
  • @Peter-ReinstateMonica With regard to preferring zero initialization, using `= default` is better as it combines with `new T()` to do just that. So if you prefer zero initialization, I would consider `= default` an improvement even if it isn't consistent with what would happen if you wrote an empty constructor yourself. – François Andrieux Nov 01 '21 at 15:48
  • Thanks for the clarification! I wasn't aware of that: An implicitly defined default ctor would do the same thing? That makes a lot more sense. I am confused because of "if T is a class type with no default constructor [...] the object is default-initialized". I thought this means the implicit case, because if there is really *no* default ctor I cannot call T()!? – Peter - Reinstate Monica Nov 01 '21 at 15:49
  • @Peter-ReinstateMonica I have to admit that the wording is unclear to me. I think it implies that if an object is not default constructible at all, it would fail due to default initialization's rules but I can't find a passage to that effect. The failure might be implied by the subsequent attempt at overload resolution which can't succeed. This seems weird to me, the only cases I can find there a class has *no* default constructor it is also not default initializable. Edit : That might be a worthwhile language-lawyer question. – François Andrieux Nov 01 '21 at 15:59
  • 1
    I asked that [here](https://stackoverflow.com/q/69800375/3150802). – Peter - Reinstate Monica Nov 01 '21 at 17:28