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
.