In the code below I show union-like class S which contains two non-related structs B and C. I show how to instantiate the non-POD std::string and delete it again and then switch S to S::CC and set the 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 say use new placement to 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 { }; // what should the init list be if I wanted 3 list elements S::BB, S::CC, S::BB?
return 0;
}
My goal, however, is to use S in an std::initializer_list. I don’t know what the format should be for initializeing h. What should the arguments be if I wanted to initialize h with these S::BB, S::CC, S::BB?
My compiler is VS2015.
Edit: This post’s history: my posting comes from a need for a definitive answer to the question of storing compile-time-deduceable heterogeneous objects in an std::initializer_list. This question has been asked many times before and there have been many attempts at answers (see Heterogeneous containers in C++). The most simplistic answer is to use polymorphism, but this ignores the power of being able to define a type at compile time (templates). Besides, heterogeneous, non-related objects grouped together polymorphically means a lot of derived data members are useless, which sows usage and maintenance confusion downstream. Other advice given was to use boost::any or boost::variant, but this has the same weakness as polymorphism and reduces message declaration clarity. Another attempt at container object heterogeneity was the use of std::tuple, but although an initializer_list can certainly contain tuples, this approach too ignores compile-time type resolution. I even found a paper written in 1999 called Heterogeneous, Nested STL Containers in C++ which uses template template arguments to solve the heterogeneity problem. After all this, I settled on class-like unions which led to my posting here. Class-like unions for non-related/heterogeneous container objects has perfect message declaration clarity, no object size ambiguity, and is compile time template-able, and it leads to excellent downstream maintenance scenarios.
Edit2: (5 weeks later) Here is what has happened. 1) I implemented a full class-like union solution given the advice in this posting. The result was tedious and unwieldy with ‘tag’ being used to identify which sub-method to call for each new functionality. Low grade regarding code maintenance. 2) c++17 has accepted std::variant. Since that is currently not yet implemented in VS2015 Update 2, I set about using boost::variant. See What is the right c++ variant syntax for calling a member function set to a particular variant? which uses the Visitor pattern to allow access to initialized variant members and member functions. This eliminates the ‘tag’ switches and variant ‘get’ calls. Bottom line: I dropped my class-like union and adopted variant for creating maintainable code that uses initializer_list to store variant member functionality all being initializable at compile time (read: highly maintainable).