Defines opDispatch as two levels of nested templates, so that the outer level only checks the member name to match any member defined by one of the contained types. This allows hasMember!TA to report proper values instead of always assuming `true`.
As a side-effect, `isOutputRange!(TA, T)` now only returns true, if any of the contained types is actually a matching output range.
Before this change, the alignment of TaggedAlgebraic was always 4, which in turn had a 50% chance of aligning 8-byte pointers on a non-8-byte boundary, causing the pointer to become invisible to the GC (and less bad, causing performance degradation when dereferencing the pointer). This would happen in particular when the total size of the TaggedUnion/TaggedAlgebraic was not divisible by 8 and an array of values was built.
- Add constructors and assignment operators for unique types
- Rename field getters to fooValue and value!() to enable sane property syntax and to avoid confusion when modifying the returned reference
- Fix misnomer "isUnionType" instead of "isUnitType"
TaggedUnion has a number of convenience features compared to TaggedAlgebraic that are possible because of the missing dynamic dispatch functionality. If the latter is not required, TaggedUnion provides a much less complex and more robust way to store a fixed set of types/kinds.
Since @disable this() still leaves an invalid .init open, TaggedAlgebraic.init has now been changed to always contain the .init value of the first specified sub type.
Note that this change is restricted to DMD 2.072 and up, because earlier versions of DMD do not allow postblits or destructors within unions and using a union appears to be the only way to implement this.
- Uses std.conv.to to implement opCast instead of just supporting implicit type conversions
- Adds .toString to make to!string(ta) return the expected result