0

I have the following code

#include <iostream>
using namespace std;

struct S{
    int a = 0;
    S() = default;
};

union U {
    S s;
    int i;
};

int main() {
    U u;
    u.s.a = 1;
    return 0;
}

However, it can't compile and issues the following errors

prog.cpp: In function ‘int main()’:
prog.cpp:15:4: error: use of deleted function ‘U::U()’
  U u;
    ^
prog.cpp:9:7: note: ‘U::U()’ is implicitly deleted because the default definition would be ill-formed:
 union U {
       ^
prog.cpp:10:4: error: union member ‘U::s’ with non-trivial ‘constexpr S::S()’
  S s;
    ^

However when I modify the definition of U by adding a default constructor, it compiles.

union U {
    S s;
    int i;
    U() {
 
    }
};

My question is why the code can't compile without a given default constructor? Do we have something in the C++ standard to explain this?

My guess is C++ prohibits a implicit default constructor if a union has non-trivial member. And a class with in-class initialization members is non-trivial. Am I right?

calvin
  • 2,125
  • 2
  • 21
  • 38

2 Answers2

1

Union cannot contain "non trivial" members: see here. "trivial" means that the member should do nothing on its constructor. Setting a to 0 is doing something, so the union cannot contain this member.

assafp
  • 69
  • 5
  • But why by adding a `U::U() {}` it can then compile? – calvin Apr 18 '22 at 06:33
  • 1
    because then S's constructor is not called. – assafp Apr 18 '22 at 06:35
  • Can I understand it like the default initialization of `U` is shadowed if I specify a custom constructor? Is this a safe or defined behavior? – calvin Apr 18 '22 at 06:40
  • yes, the default constructor IS "shadowed" by the constructor you defined. this is a defined behavior, and there should be no safety issues if it is implemented safely :-). – assafp Apr 18 '22 at 06:48
  • Where can I find more about this behavior? Since it is confusing, because after I added `U::U() {}`, `S::S()` seems [never be called](https://ideone.com/Szkaf2). – calvin Apr 18 '22 at 06:58
  • look for "constructor overloading" – assafp Apr 18 '22 at 07:05
  • I find constructor overloading means "overloaded constructors have the same name (name of the class) but the different number of arguments". I think it is something very similar to function overloading. However, I still not understand why a custom-defined `U::U() {}` will disable a custom-defined `S::S() {}`. Since it is not happen when we [use struct rather than union](https://ideone.com/4EZBgq). – calvin Apr 18 '22 at 07:35
  • the default constructor (defined by the compiler) automatically calls the constructor of all the members, while the empty default constructor U::U() {} will do nothing - and by "nothing", it mean that it will not even call the constructors of the class members, including S. – assafp Apr 22 '22 at 13:36
  • Is this behavior limited to only unions? I mean for classes, its members will still be default-initialized even if we have a custom constructor. However, for unions, if we have a custom default constructor for them, all its members will be zero initialized? – calvin Apr 22 '22 at 15:00
  • no, this is the behavior regardless being or not being in a union. hope you find this comment useful. – assafp Apr 23 '22 at 12:27
  • Well, I don't understand why it prints out "b" [in this code](https://ideone.com/4EZBgq) then. If a custom defined `A::A() {}` will shadow constructors of B such as `B::B() {}`, then I think "b" shan't have been printed. – calvin Apr 23 '22 at 15:34
0

Your union has two members. At any time, one of the two members is active, and a constructor must make one of them active, but a default constructor cannot know which one.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • If I change `int a = 0;` to `int a;`, it compiles. Is it because the compiler do zero initialization anyway? – calvin Apr 18 '22 at 06:23
  • Meanwhile, if I add the above custom `U::U() {}`, seems I still specify which member is active. Is it safe to do that? – calvin Apr 18 '22 at 06:26