如何通过可变参数模板参数列表向后迭代(递归)?

时间:2013-11-11 14:27:52

标签: c++ c++11 metaprogramming variadic-templates

前向迭代(使用递归)对我来说非常清楚:

template<typename... Ts>
struct List{
    typedef List<Ts...> Type;
    enum {
        size = sizeof...(Ts)
    };
};

template<int I>
using IntType = std::integral_constant<int, I>;

namespace Detail{
    template<int I, typename T, typename... Ts>
    struct Find : IntType<-1>{};
    template<int I, typename T, typename U, typename... Ts>
    struct Find<I, T, U, Ts...> : Find<I + 1, T, Ts...>{};
    template<int I, typename T, typename... Ts>
    struct Find<I, T, T, Ts...> : IntType<I>{};
}

template<typename T, typename U>
struct Find;
template<typename T, typename... Ts>
struct Find<T, List<Ts...>> : Detail::Find<0, T, Ts...>{};

如果我想从最后一个项目开始并向后工作,那么我会先找到最后一个项目怎么办?

我的尝试:

namespace Detail{
    template<int I, typename T, typename... Ts>
    struct ReverseFind : IntType<-1>{};
    template<int I, typename T, typename U, typename... Ts>
    struct ReverseFind<I, T, Ts..., U> : ReverseFind<I + 1, T, Ts...>{};
    template<int I, typename T, typename... Ts>
    struct ReverseFind<I, T, Ts..., T> : IntType<I>{};
}
template<typename T, typename U>
struct ReverseFind;
template<typename T, typename... Ts>
struct ReverseFind<T, List<Ts...>> : Detail::ReverseFind<sizeof...(Ts), T, Ts...>{};

这在error C3515: if an argument for a class template partial specialization is a pack expansion it shall be the last argument的MSVC2013上失败了,我认为编译器是正确的,我不能这样做(如果我错了,请纠正我)。

我可以实现一个TypeAt Meta函数,它会给我一个特定索引处的参数类型(使用递归),但它会线性复杂,如果我每次从我的ReverseFind调用它会导致在指数复杂性。

有没有办法用像Find这样的线性复杂度实现ReverseFind?

更新 更好的尝试:

namespace Detail {
    template<typename T, typename U>
    struct Reverse;
    template<typename... Us>
    struct Reverse<List<>, List<Us...>> : List<Us...>{};
    template<typename T, typename... Ts, typename... Us>
    struct Reverse<List<T, Ts...>, List<Us...>> : Reverse<List<Ts...>, List<T, Us...>>{};

}

template<typename T>
struct Reverse : Detail::Reverse<T, List<>> {};

template<typename T, typename U>
struct ReverseFind : Find<T, typename Reverse<U>::Type> {}

这在技术上是线性的复杂性,但可能还不如人们所能达到的效率高。

1 个答案:

答案 0 :(得分:1)

这很简单,可以更改Find结构以返回最大的匹配索引,而不是第一个(Live at Coliru):

template<typename... Ts>
struct List{
    typedef List<Ts...> Type;
    enum {
        size = sizeof...(Ts)
    };
};

template<int I>
using IntType = std::integral_constant<int, I>;

namespace Detail {
    template <typename T, typename U>
    struct Max : IntType<(U::value > T::value) ? U::value : T::value> {};
    template<int I, typename T, typename... Ts>
    struct FindLast : IntType<-1>{};
    template<int I, typename T, typename U, typename... Ts>
    struct FindLast<I, T, U, Ts...> : FindLast<I + 1, T, Ts...>{};
    template<int I, typename T, typename... Ts>
    struct FindLast<I, T, T, Ts...> : Max<IntType<I>, FindLast<I + 1, T, Ts...>> {};
}

template<typename T, typename U>
struct FindLast;
template<typename T, typename... Ts>
struct FindLast<T, List<Ts...>> : Detail::FindLast<0, T, Ts...>{};
相关问题