在结构的并集成员中使用std :: string时,不能引用默认构造函数

时间:2019-02-05 13:11:05

标签: 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 0;
}

使用这种结构时,我得到错误代码C2280,该默认代码无法被引用。 当我以稍微不同的方式声明该结构时,如下所示

typedef struct
{
    enum v{a,b,c}v;
    union w{
        int a;
        bool b;
        std::string c;
    }; // changed here.

}Data_Set2;

该错误不再存在。我不明白背后的原因。谁能解释为什么会这样

3 个答案:

答案 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;

破坏活动成员的提示:

  • 无法确定哪个成员处于活动状态。
  • 访问不活动的成员具有不确定的行为
  • 如果c是活动的,则不破坏它具有不确定的行为

结论:您必须确保在激活成员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::anystd::variant。后者提供类型安全的联合,在您的情况下可能是更好的选择。请注意,您的编译器(显然是MSVC)需要支持C ++ 17。

编辑:如eerorika所评论,您需要确保仅在当前活动的成员上调用它。开头链接的参考文献显示了一个字符串/向量联合的示例,以及该示例如何为未定义行为引入了许多陷阱。因此,除非您只是想了解幕后发生的事情或使用POD,否则我建议您改为使用std::variant