c++11 - using a union-like class in an std::initializer_list -


in code below show union-like class s contains 2 non-related structs b , c. show how instantiate non-pod std::string , delete again , switch s s::cc , set num int.

#include <vector> #include <string> #include <iostream> #include <memory>  struct b {   b() {}   ~b() {}   std::string str;   void func1() {} };  struct c {   c() {}   ~c() {}   int num;   void func2() {} };  struct s {   s() { tag = cc; }   s( const s& s )    {     switch( s.tag )     {       case bb:         new ( &b.str ) std::string;         b.str = s.b.str;         break;        case cc:         c.num = s.c.num;         default:         break;     }   }    ~s()    {     switch( tag )     {       case bb:         b.str.~basic_string< char >();         break;        case cc:         c.num = 0;         break;        default:         break;     }   }    enum { bb, cc } tag;   union   {     b b;     c c;   }; };  struct h {   h( std::initializer_list< s > initializerlist ) : initlistvect( initializerlist ) {}   std::vector< s > initlistvect; };  int main() {   s s;   s.tag = s::bb;   new ( &s.b.str ) std::string; // docs use new placement create memory   s.b.str = "bbb";   s.b.str.~basic_string< char >(); // string usage in b ok    s.tag = s::cc;   s.c.num = 333; // int usage in c ok    h h {  }; // should init list if wanted 3 list elements s::bb, s::cc, s::bb?    return 0; } 

my goal, however, use s in std::initializer_list. don’t know format should initializeing h. should arguments if wanted initialize h these s::bb, s::cc, s::bb?

my compiler vs2015.

edit: post’s history: posting comes need definitive answer question of storing compile-time-deduceable heterogeneous objects in std::initializer_list. question has been asked many times before , there have been many attempts @ answers (see heterogeneous containers in c++). simplistic answer use polymorphism, ignores power of being able define type @ compile time (templates). besides, heterogeneous, non-related objects grouped polymorphically means lot of derived data members useless, sows usage , maintenance confusion downstream. other advice given use boost::any or boost::variant, has same weakness polymorphism , reduces message declaration clarity. attempt @ container object heterogeneity use of std::tuple, although initializer_list can contain tuples, approach ignores compile-time type resolution. found paper written in 1999 called heterogeneous, nested stl containers in c++ uses template template arguments solve heterogeneity problem. after this, settled on class-like unions led posting here. class-like unions non-related/heterogeneous container objects has perfect message declaration clarity, no object size ambiguity, , compile time template-able, , leads excellent downstream maintenance scenarios.

edit2: (5 weeks later) here has happened. 1) implemented full class-like union solution given advice in posting. result tedious , unwieldy ‘tag’ being used identify sub-method call each new functionality. low grade regarding code maintenance. 2) c++17 has accepted std::variant. since not yet implemented in vs2015 update 2, set using boost::variant. see what right c++ variant syntax calling member function set particular variant? uses visitor pattern allow access initialized variant members , member functions. eliminates ‘tag’ switches , variant ‘get’ calls. bottom line: dropped class-like union , adopted variant creating maintainable code uses initializer_list store variant member functionality being initializable @ compile time (read: highly maintainable).

alright, i'm feeling generous , i've made custom unions myself he're stuff that'll set up. i've rewritten s structure more compliant , usable. (i've made changes marked comments)

struct s {   s() : tag(cc) // initializer   {     new (&c) c; // make c object   }    s(int num) : tag(cc) // added integer constructor   {     new (&c) c;     c.num = num;   }   s(const std::string& str) : tag(bb) // added string constructor   {     new (&b) b;      b.str = str;   }   s( const s& s ) : tag(s.tag)   {     if (tag == cc)     {       new (&c) c; // construct c       c.num = s.c.num;     }     else if (tag == bb)     {       new (&b) b; // construct b, not b.str       b.str = s.b.str;     }   }   s& operator= (const s& s) // added assignment operator   {     if (tag == s.tag) // copy b or c     {       if (tag == cc)         c = s.c;       else         b = s.b;     }     else // reconstruct b or c     {       if (tag == cc)       {         c.~c(); // destroy c         new (&b) b; // construct b         b.str = s.b.str;       }       else       {         b.~b(); // destroy b         new (&c) c; // construct c         c.num = s.c.num;       }       tag = s.tag;     }      return *this;   }    ~s()    {     if (tag == cc)     {       c.~c(); // destroy c     }     else if (tag == bb)     {       b.~b(); // destroy b, not b.str     }   }    enum { bb, cc } tag;   union   {     b b;     c c;   }; }; 

one of things doing improperly skipping construction , destruction of b , c , going straight internal variables. should create , destroy types when may trivial. while may work out, not initializing these objects asking trouble (it makes easier should change b or c in future).

to make using class easier, added in proper constructors std::string , int assignment operator. because can construct objects how want, main() this:

int main() {   s s;                    // default s   s = std::string("bbb"); // set string   s = 333;                // set number    // use initialization list   h h { std::string("bb"), 33, std::string("bb") };     return 0; } 

i encourage modify b , c use constructors build internals rather relying on s.