Получить самый внутренний тип шаблона внутри самого шаблона

возможно ли извлечь изнутри шаблон сокрытых шаблонов одного типа из одного типа? Я хотел бы получить double тип в следующем примере:

template<typename T>
struct is_a : std::false_type {};

template<typename T>
struct A
{
    using type = std::conditional_t<
        is_a<T>::value,
        T::type, // if it an A, go deeper
        T>;      // if not, we're done
};
template<typename T>
struct is_a<A<T>> : std::true_type {};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}

Это было мотивировано этим вопросом. Кроме того, я нашел этот пост, указав, что он может что-то делать с использованием ключевого слова typename или template, но я не мог заставить его работать сам.

Ответ 1

Обычный трюк, чтобы сделать это с вашим первоначальным подходом, - это отложить оценку:

template<class T> struct type_identity { using type = T; };

template<typename T>
struct A
{
    using type = typename std::conditional_t<
        is_a<T>::value,
        T,
        type_identity<T>>::type;
};

Ответ 2

Если я не пропущу что-то, я бы только частично специализировал шаблон, чтобы упростить

template<typename T>
struct A
{
    using type = T;
};

template<typename T>
struct A<A<T>>
{
    using type = typename A<T>::type;
};

int main()
{
    A<double>::type a = 5.0;
    A<A<double>>::type d = 3.0;
    A<A<A<double>>>::type c = 9.5;
    return 0;
}

Live sample

Ответ 3

Кроме того, ваш опечатка отсутствует typename, проблема здесь:

using type = std::conditional_t<
    is_a<T>::value,
    T::type, // if it an A, go deeper
    T>;      // if not, we're done

что std::conditional не является короткозамкнутым. Если T не имеет члена type, это приведет к ошибке.

Вы можете написать мета-функцию для рекурсивного извлечения внутреннего типа:

template<class T>
struct extract_type {
    using type = T;
};

template<class T> class A;

template<class T>
struct extract_type<A<T>> {
    using type = typename extract_type<T>::type;
};

template<typename T>
struct A
{
    using type = typename extract_type<T>::type;
};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}

Ответ 4

Вы можете использовать enable_if и SFINAE для выбора самого сокровенного типа:

template<typename T, class Enable = void>
struct A {
    using type = T;
};

template<typename T>
struct A<T, std::enable_if_t<!std::is_same_v<T, typename T::type>>> {
   using type = typename T::type;
};

Ответ 5

Альтернатива Марко (правильный) ответ. Вы можете поместить некоторые из этих логик выбора типа в класс признаков:

// step 1 - predeclare the template A

template<typename T> struct A;

// define a default specialisation of a traits type
template<class T> struct ATraits
{
    using type = T;
};

// specialise the traits for the A<T> case
template<class T> struct ATraits<A<T>>
{
    using type = typename A<T>::type;
};

// now define the A template default specialisation
template<typename T>
struct A
{
    using type = typename ATraits<T>::type;
};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}