26

is it possible to retrieve the innermost type of stacked templates of the same type from within the template? I'd like to retrieve the double type in the following example:

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's 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;
}

It was motivated by this question. Also, I found this post, indicating that it may have something do to with typename or template keyword placing, but I couldn't get it to work myself.

Thomas B.
  • 691
  • 4
  • 15

5 Answers5

34

Unless I'm missing something I'd just partially specialize a template to make things easier

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

Marco A.
  • 43,032
  • 26
  • 132
  • 246
11

The usual trick to do it with your original approach is to defer evaluation:

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;
};
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Gotta accept this one because it's closer to the original question. Now we have both approaches in here, this is a good thing – Thomas B. May 15 '18 at 12:11
3

Besides your typo of missing a typename, the problem here:

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

is that std::conditional is not short-circuit. When T doesn't have type member, this will cause an error.

You can write a meta function to recursively extract the inner 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;
}
llllllllll
  • 16,169
  • 4
  • 31
  • 54
1

You can use enable_if and SFINAE to select the innermost type like that:

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;
};
Jodocus
  • 7,493
  • 1
  • 29
  • 45
1

Alternative to Marco's (correct) answer. You may want to put some of this type-selection logic into a traits class:

// 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;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142