I just discovered in an answer here that a template can inherit from itself - given sufficient defined full specializations. As seen, for example, here (and live on wandbox):
#include <iostream>
#include <string>
template <typename T>
struct Foo : public Foo<decltype(T::x)> {};
template <typename TT>
struct Foo<TT*> {
TT* p;
};
template <typename TT>
struct Foo<TT&> {
TT r;
};
struct Has_x {
std::string* x;
};
int main()
{
std::string s{"I'm 's'"};
Foo<Has_x> foo_has_x;
foo_has_x.p = &s;
std::cout << typeid(foo_has_x).name() << " - " << *foo_has_x.p << std::endl;
}
(Example works for C++11 and on.)
I had no idea that was possible. It's kind of an twisted cousin of CRTP. But anyway, now I'm wondering how it can be used - or has been used.
Obviously, the answer linked above (by Columbo) was a very nice use case: Be able to find the argument types and return types of a (non-generic) functor or lambda. (And it does that by providing specializations suitable for function signatures, and inheriting using the decltype of its type parameter's operator().)
But what else can it be/has it been used for? Perhaps something not decltype-of-a-member-ish?
Also, what limitations are associated with this? I don't see, for example, how to deal (nicely, meaning by providing defaults) with any case that is not fully specialized. Your full specializations need to cover all cases. Can you provide a default case? Are there other limitations to be aware of?