Often when writing templated code, I find myself needing to store an instance of the template type in a member variable. For example, I might need to cache a value to be used later on. I would like to be able to write my code as:
struct Foo
{
template<typename T>
T member;
template<typename T>
void setMember(T value)
{
member<T> = value;
}
template<typename T>
T getMember()
{
return member<T>;
}
};
Where members are specialized as they are used. My question:
- Is such templated member variable possible with current C++ generative coding facilities?
- If not, are there any proposals for such a language feature?
- If not, are there any technical reasons why such a thing is not possible?
It should be obvious that I do not want to list all possible types (e.g. in a std::variant) as that is not generative programming and would not be possible if the user of the library is not the same as the author.
Edit: I think this somewhat answers my 3rd question from above. The reason being that today's compilers are not able to postpone instantiation of objects to after the whole program has been parsed: https://stackoverflow.com/a/27709454/3847255
This is possible in the library by combining existing facilities.
The simplest implementation would be
This is mildly inefficient since it stores each
std::type_indexobject twice (once in the key and once inside eachstd::any), so astd::unordered_set<std::any>with custom transparent hash and comparator would be more efficient; this would be more work though.Example.
As you say, the user of the library may not be the same as the author; in particular, the destructor of
Foodoes not know which types were set, but it must locate those objects and call their destructors, noting that the set of types used may be different between instances ofFoo, so this information must be stored in a runtime container withinFoo.If you're wary about the RTTI overhead implied by
std::type_indexandstd::any, we can replace them with lower-level equivalents. Forstd::type_indexyou can use a pointer to astatictag variable template instantiation (or any similar facility), and forstd::anyyou can use a type-erasedstd::unique_ptr<void, void(*)(void*)>where the deleter is a function pointer:Example. Note that once you make the deleter of
std::unique_ptra function pointer, it is no longer default-constructible, so we can't useoperator[]any more but must useinsert_or_assignandfind. (Again, we've got the same DRY violation / inefficiency, since the deleter could be used as the key into the map; exploiting this is left as an exercise for the reader.)