Inspired by this answer to a related question link, I tried the following and it seemed to work:
template<typename T>
struct HasRootType
{
private:
typedef char no;
struct yes { no m[2]; };
static T* make();
template<typename U>
static yes check(U*, typename U::root_type* = 0);
static no check(...);
public:
static bool const value = sizeof(check(make())) == sizeof(yes);
};
template<typename T, bool = HasRootType<T>::value>
struct FindRootValueType
{
typedef typename T::root_type type;
};
template<typename T>
struct FindRootValueType<T, false>
{
typedef T type;
};
// define Base, A, B, C, etc here
Thus redefining the problem to "walk up the types until you find one without a root_type
, then return that". I'm still curious why typedef-based specialisation didn't seem to work, though.