I won't steal the answer, but I will provide the method I ended up using for those who are trying to do something similar. (I am writing my own raw serialization and deserialization code with memcpy.) What I had hoped to do was store and maintain various arrangements of types without having to create a bunch of structs or classes, e.g. (from my question):
template <typename T>
class ClassThing
{
public:
T Value;
SupportedTypes Type;
}
//Then store everything in a:
vector<ClassThing> things;
However, attempting to store a templated class in a vector will give an "argument list for class template ... is missing" error, because as Tony D said in his answer, "You can't create even a single object from a template without instantiating it..." I also didn't want to use any external libraries like boost (for variants).
So, I concluded that because I absolutely wanted to use a single collection to store all of the structures, I simply could not use a templated class. Instead, I resolved to use a templated constructor (only) and a void* for the Value, as well as store the type's hash and the number of bytes required for storing/copying the type:
class ClassThing
{
public:
void* Value;
unsigned long long TypeHash;
unsigned long long NumberOfBytes;
template <typename T>
ClassThing(T passedValue)
{
Value = &passedValue;
TypeHash = typeid(passedValue).hash_code();
NumberOfBytes = sizeof(T);
}
//For strings, do this:
ClassThing(const char* passedValue, unsigned short passedNumberOfBytes)
{
Value = const_cast<char*>(passedValue);
TypeHash = typeid(char*).hash_code();
NumberOfBytes = length;
}
}
Unfortunately, this solution loses the type, but since the serialization and deserialization process I'm using is a simple memcpy, all I needed was a pointer to the data and the number of bytes it used. The reason I store the type's hash here is so that I can perform type checking before serialization (e.g. make sure a float isn't being serialized where an int should be).
For the deserialization process, I will be using this technique: https://stackoverflow.com/a/15313677/1599699
Since I do not know the type, I will simply have to expect that the cast from void* matches up with the serialization process, although I can at least check the NumberOfBytes value and ideally the TypeHash as well, if those are available. On the deserialization end, I will end up with a void* and do this:
void* deserializedData = ...;
float deserializedFloat = *(float*)&deserializedData;
This of course is not the ideal solution to my problem, but it allows me to do what I want, which is extremely high performance serialization and deserialization to binary with low memory usage and extremely low maintenance.
Hope this helps someone!