Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 anonymous union with non-trivial members

I'm updating a struct of mine and I was wanting to add a std::string member to it. The original struct looks like this:

struct Value {   uint64_t lastUpdated;    union {     uint64_t ui;     int64_t i;     float f;     bool b;   }; }; 

Just adding a std::string member to the union, of course, causes a compile error, because one would normally need to add the non-trivial constructors of the object. In the case of std::string (text from informit.com)

Since std::string defines all of the six special member functions, U will have an implicitly deleted default constructor, copy constructor, copy assignment operator, move constructor, move assignment operator and destructor. Effectively, this means that you can't create instances of U unless you define some, or all of the special member functions explicitly.

Then the website goes on to give the following sample code:

union U { int a; int b; string s; U(); ~U(); }; 

However, I'm using an anonymous union within a struct. I asked ##C++ on freenode and they told me the correct way to do that was to put the constructor in the struct instead and gave me this example code:

#include <new>  struct Point  {     Point() {}     Point(int x, int y): x_(x), y_(y) {}     int x_, y_; };  struct Foo {   Foo() { new(&p) Point(); }   union {     int z;     double w;     Point p;   }; };  int main(void) { } 

But from there I can't figure how to make the rest of the special functions that std::string needs defined, and moreover, I'm not entirely clear on how the ctor in that example is working.

Can I get someone to explain this to me a bit clearer?

like image 673
OmnipotentEntity Avatar asked May 21 '12 23:05

OmnipotentEntity


People also ask

What are the restrictions that must be observed when using C++ unions?

A union cannot have base classes and cannot be used as a base class. A union cannot have non-static data members of reference types. Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).

Does non-trivial copy constructor?

An object of a class with a non-trivial constructor, a non-trivial copy constructor, a non-trivial destructor, or a non-trivial copy assignment operator cannot be a member of a union. Hence, members of a union can't have constructors, destructors, virtual member functions, or base classes.

What is anonymous union in C?

Anonymous unions/structures are also known as unnamed unions/structures as they don't have names. Since there is no names, direct objects(or variables) of them are not created and we use them in nested structure or unions. Definition is just like that of a normal union just without a name or tag.

What is a non-trivial constructor?

If you define a constructor yourself, it is considered non-trivial, even if it doesn't do anything, so a trivial constructor must be implicitly defined by the compiler.


1 Answers

There is no need for placement new here.

Variant members won't be initialized by the compiler-generated constructor, but there should be no trouble picking one and initializing it using the normal ctor-initializer-list. Members declared inside anonymous unions are actually members of the containing class, and can be initialized in the containing class's constructor.

This behavior is described in section 9.5. [class.union]:

A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members; otherwise, its variant members are the non-static data members of all anonymous unions that are members of X.

and in section 12.6.2 [class.base.init]:

A ctor-initializer may initialize a variant member of the constructor’s class. If a ctor-initializer specifies more than one mem-initializer for the same member or for the same base class, the ctor-initializer is ill-formed.

So the code can be simply:

#include <new>  struct Point  {     Point() {}     Point(int x, int y): x_(x), y_(y) {}     int x_, y_; };  struct Foo {   Foo() : p() {} // usual everyday initialization in the ctor-initializer   union {     int z;     double w;     Point p;   }; };  int main(void) { } 

Of course, placement new should still be used when vivifying a variant member other than the other initialized in the constructor.

like image 148
Ben Voigt Avatar answered Sep 28 '22 02:09

Ben Voigt