我有一个非常基本的结构,具有一个枚举和一个联合。
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;
}
使用这种结构时,我得到错误代码C2280,该默认代码无法被引用。 当我以稍微不同的方式声明该结构时,如下所示
typedef struct
{
enum v{a,b,c}v;
union w{
int a;
bool b;
std::string c;
}; // changed here.
}Data_Set2;
该错误不再存在。我不明白背后的原因。谁能解释为什么会这样
答案 0 :(得分:2)
在第一个示例中,您定义了一个成员变量,该变量必须是默认可构造的。在第二个示例中,您定义了一个成员 type ,如果您使用它来定义该类型的变量,则会出现相同的错误。
对于错误,您需要在联合中创建一个默认构造函数以能够正确初始化它:
union w{
int a;
bool b;
std::string c;
// Default constructor initialize the string member
w() : c()
{}
}w;
答案 1 :(得分:2)
问题在于联合w
既不是默认可构造的,也不是可破坏的。默认构造函数和析构函数不是隐式生成的,因为成员c
不可平凡地构造,也不可平凡地被破坏。因此,根本不可能拥有类型w
的成员。在第二个示例中,删除了该成员,所以没有问题。
为了使w
可以默认构造,可以定义一个默认构造函数:
union w{
int a;
bool b;
std::string c;
w() // Could activate one of the members if so desired
{}
为了使w
可破坏,可以定义一个析构函数(但要读到最后):
~w(){
//TODO destruct the active member
}
} w;
破坏活动成员的提示:
结论:您必须确保在激活成员w
时永不破坏c
。假设Data_Set2
指示哪个成员处于活动状态(应该保留另一个不变式;这些成员可能不应该公开),则可以在v
的析构函数中实现这种不变式。
答案 2 :(得分:2)
来自https://en.cppreference.com/w/cpp/language/union:
如果联合包含具有非平凡特殊成员函数(复制/移动构造函数,复制/移动分配或析构函数)的非静态数据成员,则该函数默认在联合中被删除并需要定义由程序员明确表示。
如果联合包含具有非平凡默认构造函数的非静态数据成员,则默认情况下将删除联合的默认构造函数,除非联合的变体成员具有默认成员初始化程序。
最多一个变体成员可以具有默认成员初始化器。
在您的情况下,这意味着您必须显式声明一个构造函数和解码器。将您的代码更改为:
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;
这应该有效。
正如我在评论中已经指出的那样,您应该看看std::any
和std::variant
。后者提供类型安全的联合,在您的情况下可能是更好的选择。请注意,您的编译器(显然是MSVC)需要支持C ++ 17。
编辑:如eerorika所评论,您需要确保仅在当前活动的成员上调用它。开头链接的参考文献显示了一个字符串/向量联合的示例,以及该示例如何为未定义行为引入了许多陷阱。因此,除非您只是想了解幕后发生的事情或使用POD,否则我建议您改为使用std::variant
。