Default constructor cannot be referenced when using std::string in union member of a struct

淺唱寂寞╮ 提交于 2021-02-04 18:28:25

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!