Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Default constructors in union with variant member with non-trivial default constructor

I recently read a description of default constructors for unions: Default Constructor

There is a following rule:

Blockquote Deleted implicitly-declared default constructor: [...] T is a union with at least one variant member with non-trivial default constructor, and no variant member of T has a default member initializer.[...]

Then I decided to make an exercise and verify the rule.

struct Member {
 public:
  // Member() : mX(0) {}
  virtual int GetX() {
    return mX;
  }
  int mX;
};

union DefaultConstructor {
  int mA;
  Member mMember;
  int GetA();
};

With use of gcc v5.3.1 (I know it is pretty old) I receive expected error:

> ../src/DefaultConstrcutors.cpp: In function ‘void
> Test_DefaultConstructors()’: ../src/DefaultConstrcutors.cpp:26:22:
> error: use of deleted function
> ‘DefaultConstructor::DefaultConstructor()’    DefaultConstructor foo;
>                       ^ In file included from ../src/DefaultConstrcutors.cpp:19:0:
> ../src/DefaultConstructors.h:155:7: note:
> ‘DefaultConstructor::DefaultConstructor()’ is implicitly deleted
> because the default definition would be ill-formed:  union
> DefaultConstructor {
>        ^ ../src/DefaultConstructors.h:157:10: error: union member ‘DefaultConstructor::mMember’ with non-trivial ‘Member::Member()’   
> Member mMember;
>           ^

Ok, so according to rule, if I provide a default member initializer for a variant member, then it should compile. So I've changed union definition to:

union DefaultConstructor {
      int mA = 0;
      Member mMember;
      int GetA();
    };

And now it should compile, but I've received the same error.

Next step was to provide default initializer for a mMember instead of mA (only one union variant member may have default initializer).

union DefaultConstructor {
      int mA;
      Member mMember{};
      int GetA();
    };

Now it compiles.

The question is: why it didn't compile in second case, when mA had default initializer? According to mentioned rule it should be possible. What more similar rule is provided here: Union declaration

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 .

Anyone has an idea why doesn't it work?

Greetings, Piotr

like image 842
Piotr Avatar asked May 18 '18 06:05

Piotr


1 Answers

cppreference is generaly a nice information source, but here the compiler is right. Draft n3337 for C++11 contains in 9.5 Unions [class.union] a non normative but explicit note saying:

If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union.

There is no mention that is will not be the case is one member has a default member initializer.

The same note still stands in draft n4296 for C++14 so I assume it should still be the same in the actual C++ standard. Notes are of course non normative, but their intent is to make more clear an interpretation of the standard, so I assume that the interpretation of cppreference is wrong because it does not respect that note, and that gcc interpretation is correct.

like image 176
Serge Ballesta Avatar answered Sep 30 '22 00:09

Serge Ballesta