在模板本身中检索最里面的模板类型

时间:2018-05-15 07:05:05

标签: c++ templates types

是否可以从模板中检索最内层类型的相同类型的堆叠模板?我想在以下示例中检索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'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;
}

这是question的动机。另外,我发现了这个post,表明它可能会对typenametemplate关键字的位置产生影响,但我无法让它自己工作。

5 个答案:

答案 0 :(得分:34)

除非我遗漏某些内容,否则我只是部分专门化模板以简化操作

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

答案 1 :(得分:11)

使用原始方法执行此操作的常用技巧是推迟评估:

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 :(得分:3)

除了错过typename的错字外,问题还有:

using type = std::conditional_t<
    is_a<T>::value,
    T::type, // if it's 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;
}

答案 3 :(得分:1)

您可以使用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;
};

答案 4 :(得分:1)

替代马可(正确)答案。您可能希望将一些此类型选择逻辑放入traits类中:

// 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;
}