C++ 在结构的联合成员中使用std::string时,无法引用默认构造函数

C++ 在结构的联合成员中使用std::string时,无法引用默认构造函数,c++,struct,constructor,C++,Struct,Constructor,我有一个非常基本的结构,它有一个枚举和一个并集 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

我有一个非常基本的结构,它有一个枚举和一个并集

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;

错误不再存在。我不明白这背后的原因。有人能解释一下为什么会发生这种情况吗?在第一个示例中,您定义了一个成员变量,它需要是默认可构造的。在第二个示例中,您定义了一个成员类型,如果您使用它来定义该类型的变量,则会出现相同的错误

对于错误,您需要在union中创建一个默认构造函数,以便能够正确初始化它:

union w{
    int a;
    bool b;
    std::string c;

    // Default constructor initialize the string member
    w() : c()
    {}
}w;

问题是union
w
既不是默认的可构造的,也不是可破坏的。默认构造函数和析构函数不是隐式生成的,因为成员
c
不是可构造的,也不是可破坏的。因此,拥有
w
类型的成员是不可能的。在第二个示例中,删除了成员,因此没有问题

为了使
w
default可构造,您可以定义一个默认构造函数:

union w{
    int a;
    bool b;
    std::string c;

    w() // Could activate one of the members if so desired
    {}
为了使
w
可销毁,您可以定义一个析构函数(但读取到最后):

销毁活动成员的提示:

  • 无法找出哪个成员处于活动状态
  • 访问非活动成员具有未定义的行为
  • 如果c是活动的,不破坏它有未定义的行为
总之:当成员
c
处于活动状态时,必须确保
w
不会被销毁。假设
v
指示哪个成员处于活动状态(这是另一个应该维护的不变量;这些成员可能不应该是公共的),则可以在
数据集2的析构函数中实现这种不变量

如果联合体包含一个非静态数据成员,该成员具有一个非平凡的特殊成员函数(复制/移动构造函数、复制/移动赋值或析构函数),则该函数在联合体中默认删除,并且需要由程序员显式定义

如果联合包含具有非平凡默认构造函数的非静态数据成员,则默认情况下将删除联合的默认构造函数,除非联合的变体成员具有默认成员初始值设定项

最多一个变量成员可以有一个默认成员初始值设定项

在您的情况下,这意味着您必须显式声明构造函数和析构函数。将代码更改为:

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

@一些程序员。即使使用了您提供的代码,我仍然会遇到相同的错误。@eerorika默认成员初始值设定器也无法解决此问题。旁注:请查看和。后者提供类型安全的联合,在您的情况下可能是更好的选择。请注意,您的编译器(显然是MSVC)需要支持C++17。关于“这应该可以工作。”:当对象被销毁时,
C
处于活动状态时会发生什么情况?@eerorika:哦,这是真的。我猜您必须明确地调用成员析构函数,但我不确定,因此我不想更新我的答案。假设你有
数据集2 x,我假设您必须调用
x.c.~basic_string()(?)。我将尝试对此做进一步的研究。我认为答案是:
Data\u Set2
的析构函数必须确保
w.c
在销毁时永远不会激活。
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;