Why does a struct, that has another struct wrapped in a union as a member not compile without an explicit default constructor?
Asked Answered
H

1

3

This is the relationship I am talking about:

struct A{
    int i = 1;
};

struct B{
    union{A a;};
};

void main(){
    B b;
};

In this constellation, my compiler (vs2015) complains about the default constructor of B B::B(void) beeing deleted, with the note that the compiler has generated B::B:

../test.cpp(155): error C2280: "B::B(void)" : Es wurde versucht, auf eine gelöschte Funktion zu verweisen
../test.cpp(152): note: Compiler hat hier "B::B" generiert

(sorry, I could not convince msvc to talk english to me)

Either of these two code changes fixes it:

struct A{
    int i; //removed initialzation of member to 1
};

or

struct B{
    B(){} //adding explicit default constructor
    union{A a;};
};

I know that adding a default constructor that does nothing is not exactly a complicated or annoying workaround, but I really want to know why C++ forces me to do this.

Humanism answered 4/11, 2018 at 17:8 Comment(0)
D
3

This is because of [class.default.ctor]p2 which says:

A defaulted default constructor for class X is defined as deleted if:

  • (2.1) X is a union that has a variant member with a non-trivial default constructor and no variant member of X has a default member initializer,

.....

and we can see why A does not have a trivial default constructor from [class.default.ctor]p3 which says:

A default constructor is trivial if it is not user-provided and if:
- (3.1) — its class has no virtual functions (10.6.2) and no virtual base classes (10.6.1), and
- (3.2) —no non-static data member of its class has a default member initializer (10.3), and
- (3.3) — all the direct base classes of its class have trivial default constructors, and
- (3.4) — for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

We can see from this live godbolt example that removing the non-static member initializer makes the program well formed.

struct A{
    int i ; // no NSDMI
};

struct B{
    union{A a;};
};

The paper that resulted in the wording we have today is n2544:1 Unrestricted Unions (Revision 2), it covers the rational here:

We have also changed the way implicitly declared special member functions of unions are generated in the following way: if a non-trivial special member function is defined for any member of a union, or a member of an anonymous union inside a class, that special member function will be implicitly deleted (8.4 ¶10) for t he union or class. This prevents the compiler from trying to write code that it cannot know how t o write, and forces the programmer to write that code if it’s needed. The fact that the compiler can’t write such a function is no reason not to let the programmer do so.

A union has only one active member, if one or more of the members can not be default constructed how should the compiler choose which one to make active by default?

Deppy answered 4/11, 2018 at 17:29 Comment(3)
Do you know the rationale for this rule?Ezequiel
Well, if it can't decide which one to make active by default this is totally reasonable. However, in my minimal example, there is only one member in the union, which can be default constructed. In my actual use case, I have two variables of the same type in there, which also should not cause a problem by that logic.Humanism
@Humanism probably they thought it was not worth specifying for a case that was not very useful.Deppy

© 2022 - 2024 — McMap. All rights reserved.