更改包的模板

时间:2016-04-22 17:27:39

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

鉴于

template <typename...> class P;  template <typename...> class Q;
template <typename...> class R;  template <typename...> class S;

让我们定义change_templates,如下例所示:

change_templates<P<int, char, R<double, int>, bool>, P,Q, R,S>::type

Q<int, char, S<double, int>, bool>

更一般地说,

change_templates<P<int, Q<int, int>, char, P<double, P<char, char>, int>, bool>, P,Q, P,R, Q,S>::type

Q<int, S<int, int>, char, R<double, P<char, char>, int>, bool>

(请注意,缺少第三个P,...对意味着第三个P保持不变,但无论如何都可以使用P,P。好消息是我使用以下内容:

#include <iostream>
#include <type_traits>

template <template <typename...> class...> struct TP;

template <template <typename...> class, typename Templates, typename Checked = TP<>> struct find_template;

template <template <typename...> class P, template <typename...> class Q,
    template <typename...> class... Rest, template <typename...> class... Checked>
struct find_template<P, TP<Q, Rest...>, TP<Checked...>> :
    find_template<P, TP<Rest...>, TP<Checked..., Q>> {};  // Search the next template by default.

template <template <typename...> class P, template <typename...> class Q,
    template <typename...> class... Rest, template <typename...> class... Checked>
struct find_template<P, TP<P, Q, Rest...>, TP<Checked...>> {  // Q follows P, which means that P is to change to Q.
    template <typename... Ts>
    using type = Q<Ts...>;
    using remaining_templates = TP<Checked..., Rest...>;  // Every template except P,Q kept for later searches, since they have been used now.
};

template <template <typename...> class P, template <typename...> class... Checked>
struct find_template<P, TP<>, TP<Checked...>> {  // P not found.
    template <typename... Ts>
    using type = P<Ts...>;  // P not found, so simply don't change P.
    using remaining_templates = TP<Checked...>;
};

template <typename T, template <typename...> class... Templates>
struct change_templates {
    using type = T;  // Base case.  A non-pack type remains unchanged.
};

template <typename T, typename Templates> struct change_templates_impl;

template <typename T, template <typename...> class... Templates>
struct change_templates_impl<T, TP<Templates...>> :
    change_templates<T, Templates...> {};

template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct change_templates<P<Ts...>, Templates...> {  // A pack of types (possibly of other packs).
    using F = find_template<P, TP<Templates...>>;
    using Remaining = typename F::remaining_templates;  // To be passed on for the next find_template call.
    using type = typename F::template type<typename change_templates_impl<Ts, Remaining>::type...>;  // Recursive call.
};

// Testing
template <typename...> class P;  template <typename...> class Q;
template <typename...> class R;  template <typename...> class S;

int main() {
    static_assert (std::is_same<change_templates<P<int, char, double>, P, Q>::type, Q<int, char, double>>::value, "");

    static_assert (std::is_same<
        change_templates<P<int, char, R<double, int>, bool>, P,Q, R,S>::type,
        Q<int, char, S<double, int>, bool>
    >::value, "");

    static_assert (std::is_same<
        change_templates<P<int, Q<int, int>, char, P<double, P<char, char>, int>, bool>, P,Q, P,R, Q,S>::type,  // The third P in the pack is unchanged because there is no pair for the third P.
        Q<int, S<int, int>, char, R<double, P<char, char>, int>, bool>
    >::value, "");
}

但现在我自然想要以下内容:

template <int...> class I;  template <int...> class J;

int main() {
    static_assert (std::is_same<
        change_templates<P<int, char, I<4,5>, bool>, P,Q, I,J>::type,
        Q<int, char, J<4,5>, bool>
    >::value, "");
}

但是我不知道如何调整上面的代码来处理这个问题,因为P,Q是类型的模板,而I,J是int的模板(或者更一般地是任何整数类型的模板)。有没有什么可以使不同类型的模板同质化,以便上述内容可以按预期工作(或者语法略有不同)?如果我们在同一个包装中同时拥有int包和std::size_t包等,该怎么办?

1 个答案:

答案 0 :(得分:0)

以下解决方案对我来说是半满意的。现在至少将所有不同类型的模板均匀化为类型,并且现在满足特定的静态断言。如果有人可以指导我如何在int的最后部分专业化中概括change_templates,则可能会删除&#34;不可维护的&#34; Murat Karakus评论的解决方案。

#include <iostream>
#include <type_traits>
#include <tuple>

template <template <typename...> class...> struct K;
template <typename T, template <T...> class...> struct L;

template <typename Find, typename Templates, typename Checked = std::tuple<>> struct find_template;

template <typename FindMe, template <typename...> class Z, typename Mismatch, typename... Rest, typename... Checked>
struct find_template<FindMe, Z<Mismatch, Rest...>, Z<Checked...>> : find_template<FindMe, Z<Rest...>, Z<Checked..., Mismatch>> {};  // Search the next template by default.

template <template <typename...> class P, template <typename...> class Z, template <typename...> class Q, typename... Rest, typename... Checked>
struct find_template<K<P>, Z<K<P,Q>, Rest...>, Z<Checked...>> {  // Q follows P, which means that P is to change to Q.
    template <typename... Ts>
    using type = Q<Ts...>;
    using remaining_templates = Z<Checked..., Rest...>;  // Every template except P,Q kept for later searches, since they have been used now.
};

template <typename T, template <T...> class P, template <typename...> class Z, template <T...> class Q, typename... Rest, typename... Checked>
struct find_template<L<T,P>, Z<L<T,P,Q>, Rest...>, Z<Checked...>> {  // Q follows P, which means that P is to change to Q.
    template <T... Is>
    using type = Q<Is...>;
    using remaining_templates = Z<Checked..., Rest...>;  // Every template except P,Q kept for later searches, since they have been used now.
};

template <template <typename...> class P, template <typename...> class Z, typename... Checked>
struct find_template<K<P>, Z<>, Z<Checked...>> {  // P not found.
    template <typename... Ts>
    using type = P<Ts...>;  // P not found, so simply don't change P.
    using remaining_templates = Z<Checked...>;
};

template <typename T, typename... Templates>
struct change_templates {
    using type = T;  // Base case.  A non-pack type remains unchanged.
};

template <typename T, typename Templates> struct change_templates_impl;

template <typename T, template <typename...> class Z, typename... Templates>
struct change_templates_impl<T, Z<Templates...>> : change_templates<T, Templates...> {};

template <template <typename...> class P, typename... Ts, typename... Templates>
struct change_templates<P<Ts...>, Templates...> {  // A pack of types (possibly of other packs).
    using F = find_template<K<P>, std::tuple<Templates...>>;
    using Remaining = typename F::remaining_templates;  // To be passed on for the next find_template call.
    using type = typename F::template type<typename change_templates_impl<Ts, Remaining>::type...>;  // Recursive call.
};

template <template <int...> class P, int... Is, typename... Templates>
struct change_templates<P<Is...>, Templates...> {  // A pack of integral non-types.
    using F = find_template<L<int,P>, std::tuple<Templates...>>;
    using type = typename F::template type<Is...>;  // Non-recursive call, since the Is... cannot themselves be packs.
};

// Testing
template <typename...> class P;  template <typename...> class Q;
template <typename...> class R;  template <typename...> class S;
template <int...> class I;  template <int...> class J;

int main() {
    static_assert (std::is_same<change_templates<P<int, char, double>, K<P,Q>>::type, Q<int, char, double>>::value, "");

    static_assert (std::is_same<
        change_templates<P<int, char, R<double, int>, bool>, K<P,Q>, K<R,S>>::type,
        Q<int, char, S<double, int>, bool>
    >::value, "");

    static_assert (std::is_same<
        change_templates<P<int, Q<int, int>, char, P<double, P<char, char>, int>, bool>, K<P,Q>, K<P,R>, K<Q,S>>::type,  // The third P in the pack is unchanged because there is no K pair for the third P.
        Q<int, S<int, int>, char, R<double, P<char, char>, int>, bool>
    >::value, "");

    static_assert (std::is_same<
        change_templates<P<int, char, I<4,5>, bool>, K<P,Q>, L<int,I,J>>::type,
        Q<int, char, J<4,5>, bool>
    >::value, "");
}

更新:我意识到无法完成对int的推广,因为任何编译器都可以从函数中推导出T

template <typename T, template <T...> class P, T... Is>
T foo(P<Is...>);

所以

template <template <int...> class P, int... Is, typename... Templates>
struct change_templates<P<Is...>, Templates...>

需要对其他整数类型重复。