Peut-être que cela peut être fait d'une manière plus simple... Quoi qu'il en soit, je propose une solution de répartition des balises / SFINAE.
Tout d'abord, un simple récursif tag
struct
template <std::size_t N>
struct tag : public tag<N-1u>
{ };
template <>
struct tag<0u>
{ };
pour éviter les ambiguïtés dans les cas où plus d'un des noms de type possibles sont définis.
Ensuite, une fonction modèle (seulement déclarée) pour chaque type que vous voulez extraire des types possibles ; une pour type
template <typename T, std::void_t<typename T::type>* = nullptr>
typename T::type getType (tag<0u>);
un pour this_type
template <typename T, std::void_t<typename T::this_type>* = nullptr>
typename T::this_type getType (tag<1u>);
un pour ThisType
template <typename T, std::void_t<typename T::ThisType>* = nullptr>
typename T::ThisType getType (tag<2u>);
et un (pour être un peu idiot) pour MySillyTypeName
template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
typename T::MySillyTypeName getType (tag<3u>);
Observez que le nombre de tag
sont différents : cela évite les ambiguïtés possibles et donne un ordre de priorité pour les noms.
Maintenant, une structure triviale qui utilise getType()
pour extraire le type requis
template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
struct GetType { using type = U; };
Voici un exemple de compilation complète de C++17
#include <type_traits>
template <std::size_t N>
struct tag : public tag<N-1u>
{ };
template <>
struct tag<0u>
{ };
template <typename T, std::void_t<typename T::type>* = nullptr>
typename T::type getType (tag<0u>);
template <typename T, std::void_t<typename T::this_type>* = nullptr>
typename T::this_type getType (tag<1u>);
template <typename T, std::void_t<typename T::ThisType>* = nullptr>
typename T::ThisType getType (tag<2u>);
template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
typename T::MySillyTypeName getType (tag<3u>);
template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
struct GetType { using type = U; };
struct foo1 { using type = short; };
struct foo2 { using this_type = int; };
struct foo3 { using ThisType = long; };
struct foo4 { using MySillyTypeName = long long; };
int main()
{
static_assert( std::is_same_v<short, GetType<foo1>::type> );
static_assert( std::is_same_v<int, GetType<foo2>::type> );
static_assert( std::is_same_v<long, GetType<foo3>::type> );
static_assert( std::is_same_v<long long, GetType<foo4>::type> );
}