On Fri, Jan 9, 2015 at 1:24 PM, Larry Evans
Which seems simpler to me. After all, implementing the
discriminated union involves, at first, just the "primitives":
I've implemented variant on top of discriminated_union and I've implemented discriminated_union on top of variant. To be honest, both solutions leave a lot to be desired although neither is particularly difficult to do. The types I actually use aren't built on one another, but are built on shared, lower-level constructs. 1) calculating the size an alignment of a buffer
You actually don't do this when implementing a discriminated union type (nor variant) anymore. You need to use an *actual* union underneath the hood in order for some degree of constexpr-ness in places that it is possible. This is one reason for the union_ and destructible_union templates in my earlier reply (they use a cons-like recursive union structure underneath the hood, which is an approach that Eric Niebler suggested and that I've seen done elsewhere as well). They are necessary as implementation details and useful on their own when discrimination isn't required. They support fully forwarded construction of the members along with access by index, etc. 2) using the tag to find the right type in a type list as
in the mpl::at_c<tag>:
Using variant, step 1 is the same, but how is step 2 implemented?
I'm sure you are making a good point but I'm having trouble understanding what specifically you are saying here. To be clear, I agree that in theory it makes more sense to implement variant on top of discriminated_union, but having implemented each on top of the other in the past, they both have different problems, though neither is too difficult to do and you don't lose any functionality. It just ends up being that the implementation still has some complexities and the compile-times take a noticeable hit, so building them each from exactly their shared requirements ends up working better.
Could you describe the lower-level primitives?
The union_ and destructible_union templates, along with an unfortunately complicated set of chained CRTP bases that are required to guarantee that each of the special member functions are individually declared and defined (and possibly trivial) exactly when they can be. I didn't elaborate earlier, but the reason destructible_union exists is because with the way C++ union rules are, if any of the members has a destructor that is non-trivial, the overall union itself is not descructible at all. This "not-defined-if-any-are-nontrivial" is true of all of the special member functions with respect to modern unions, but allowing destructibility is important here because while you can encapsulate a type that is, for instance, not copyable, and then give the containing type a copy constructor, you cannot encapsulate a type that is not destructible and then give the containing type a destructor. Instead, the union itself needs to be destructible. So, destructible_union is equivalent to union_ in cases where all of the destructors were trivial, but in the case that any of them are not trivial, it defines its own destructor that does nothing. This way it can be used when implementing a variant or a discriminated_union (or other constructs). -- -Matt Calabrese