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?
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…