问题
I have a very basic struct that has an enum and a union.
typedef struct
{
enum v{a,b,c}v;
union w{
int a;
bool b;
std::string c;
}w;
}Data_Set2;
int main()
{
Data_Set2 val; // Shows errror that the default constructor cannot be referenced
return 0;
}
On using such a struct I get the Error Code C2280 that the default constructor cannot be referenced. When I declare the struct in a slightly different way as following
typedef struct
{
enum v{a,b,c}v;
union w{
int a;
bool b;
std::string c;
}; // changed here.
}Data_Set2;
The Error no longer exists. I fail to understand the reason behind this. Could anyone explain why this happens
回答1:
From https://en.cppreference.com/w/cpp/language/union (or see standard):
If a union contains a non-static data member with a non-trivial special member function (copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.
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 .
At most one variant member can have a default member initializer.
In your case this means that you have to explicitly declare a constructor and destructor. Change your code to:
typedef struct
{
enum v{a,b,c} v;
union w{
int a;
bool b;
std::string c;
w() {} // Explicit constructor definition
~w() { }; // Explicit destructor definition
} w;
} Data_Set2;
This should work.
As already stated in my comment, you should however have a look at std::any and std::variant. The latter provides type-safe unions and would probably be the better choice in your case. Note that your compiler (apparently MSVC) needs to support C++17.
EDIT: As commented by eerorika, you will need to make sure that you only call it on the currently active member. The reference linked in the beginning shows an example for a string/vector union and how it introduces many pitfalls for undefined behavior. So unless you're just trying to understand what's happening behind the scenes or using POD types, I'd advise you work with std::variant instead.
回答2:
The problem is that the union w is neither default constructible, nor is it destructible. The default constructor and the destructor are not implicitly generated, because the member c is not trivially constructible, nor trivially destructible. As such, having a member of type w is simply not possible. In your second example, you remove the member, so there is no problem.
In order to make w default constructible, you can define a default constructor:
union w{
int a;
bool b;
std::string c;
w() // Could activate one of the members if so desired
{}
In order to make w destructible, you can define a destructor (but read until the end):
~w(){
//TODO destruct the active member
}
} w;
Hints for destructing the active member:
- It is not possible to find out which member is active.
- Accessing an inactive member has undefined behaviour
- If c is active, not destroying it has undefined behaviour
In conclusion: You must make sure that w is never destroyed while having the member c active. Such invariant could be implemented in the destructor of Data_Set2, assuming v indicates which member is active (which is another invariant that should be maintained; those members should probably not be public).
回答3:
In the first example you define a member variable, which needs to be default constructible. In the second example you define a member type, which will give the same error if you use it to define a variable of that type.
As for the error, you need to create a default constructor in the union to be able to initialize it correctly:
union w{
int a;
bool b;
std::string c;
// Default constructor initialize the string member
w() : c()
{}
}w;
来源:https://stackoverflow.com/questions/54535195/default-constructor-cannot-be-referenced-when-using-stdstring-in-union-member