The following code:
struct non_trivially {
non_trivially() {};
};
union U {
bool dummy{false};
non_trivially value;
};
int main() {
U tmp;
}
https://godbolt.org/z/1cMsqq9ee
Produces next compiler error on clang (13.0.0):
source>:11:7: error: call to implicitly-deleted default constructor of 'U'
U tmp;
^ <source>:7:19: note: default constructor of 'U' is implicitly deleted because variant field 'value' has a non-trivial default constructor
non_trivially value;
But successfully compiles using MSVC (19.30).
According to the cppreference it should be a valid code: https://en.cppreference.com/w/cpp/language/union
If a union contains a non-static data member with a non-trivial default constructor, the default constructor of the union is deleted by default unless a variant member of the union has a default member initializer .
In my example there is an alternative in U with a default member initializer so default constructor shouldn't be deleted, but it is. What am I missing?
CodePudding user response:
This is CWG2084. Clang and gcc are just wrong in deleting the default constructor if you provide a default member initializer.
The relevant quote that was added to the standard after the change was adopted (in [class.default.ctor]):
A defaulted default constructor for class X is defined as deleted if:
- 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,
- [...]
- X is a union and all of its variant members are of const-qualified type (or array thereof),
(being the only two points that refer to union types, neither one applies, so it should not be implicitly deleted)
The workaround is to user-provide a constructor:
union U {
constexpr U() : dummy{false} {}
bool dummy;
non_trivially value;
};
// Or this also seems to work
union U {
constexpr U() {}
bool dummy{false};
non_trivially value;
};
CodePudding user response:
It's all slightly mysterious. gcc behaves the same as clang.
The standard has this to say (emphasis mine):
Absent default member initializers, if any non-static data member of a union has a non-trivial default constructor, copy constructor, move constructor, copy assignment operator, move assignment operator, or destructor, the corresponding member function of the union must be user-provided or it will be implicitly deleted for the union.
But I think the wording is a bit wooly here. I think what they actually mean is that you must provide an initialiser for the member that has (in your example) a non-trivial constructor, like so:
struct non_trivially {
non_trivially() {};
};
union U {
bool dummy;
non_trivially value { }; // <-- note that I have added an initialiser
};
int main() {
U tmp;
}
Then, it compiles.