In C, unions are not typesafe and requires that the programmer be completely responsible with how the unions are used. Ada’s discriminated variant records are a typesafe way of doing unions and using the Internals pattern1 we can get the same functionality in C++. This is all the code that’s needed:
template<typename...>
struct Internals
{
// Empty struct for the default case
};
Due to C++’s template instantiation rules, you can have different members of a class for any specialization of the template. In essence:
template<>
struct Internals<int>
{
int int_data; // Note that there is no requirement that the members are the same
};
template<>
struct Internals<std::string>
{
std::string string_data;
};
template<>
struct Internals<int, int>
{
std::tuple<int, int> int_tuple_data;
};
All of these structs are completely different types, even though they all come from the Internals templated type. They bear no relation to each other in the way inheritance would have done, and this is the main benefit. You can have compile-time polymorphism that is checked before you even run the code. This is now possible:
template<typename T, typename U, typename... Vs>
struct Scaffold
{
Internals<Scaffold> internals;
void print()
{
std::cout << "Default dummy data" << std::endl;
}
};
template<>
struct Internals<Scaffold<int, float, char, double>>
{
std::string data{"Specialized dummy data"};
};
template<>
void Scaffold<int, float, char, double>::print()
{
std::cout << this->internals.data << std::endl;
}
int main()
{
Scaffold<long, long>{}.print();
Scaffold<int, float, char, double>{}.print();
return 0;
}
The output of this program is:
Default dummy data
Specialized dummy data
This is a useful behaviour when you want something akin to aspects, or crosscutting concerns. Clients of your framework can specialize the internals without having to derive from any framework class, decoupling the framework scaffolding from the client.