【问题标题】:How to replace a template parameter in a given type?如何替换给定类型的模板参数?
【发布时间】:2018-07-11 20:18:29
【问题描述】:

鉴于名为B 的模板参数的类型为C<D>(在实例化时),我如何从B 构造C<A>

这是我的代码的最小化摘录:

template<typename A>
class foo {
    template<typename B> // B is guaranteed to always be of the form C<D>
    // How to write a function here with return type C<A>?
}

【问题讨论】:

    标签: c++ templates c++14 template-meta-programming


    【解决方案1】:

    您可以使用 template specializationtemplate template parameter 来做到这一点

    template<typename, typename>
    struct meta {};
    
    template<typename A, template<typename> typename C, typename B>
    struct meta<A, C<B>> {
        using type = C<A>;
    };
    

    meta&lt;A, C&lt;B&gt;&gt;::type 将是 C&lt;A&gt;

    如果你想在基本情况下处理默认参数

    template<typename...>
    struct meta {};
    
    template<typename A, template<typename...> typename C, typename B, typename ... Ts>
    struct meta<A, C<B, Ts...>> {
        using type = C<A, Ts...>;
    };
    

    【讨论】:

    • 非常感谢;我阅读了这两个答案,对我来说这是最有效的方法(以避免 STL 分配器混乱):template&lt;typename...&gt; struct meta {}; template&lt;typename A, template&lt;typename...&gt; typename C, typename B, typename ... Ts&gt; struct meta&lt;A, C&lt;B, Ts...&gt;&gt; { using type = C&lt;A&gt;; /*but we drop the template arguments after the first (thus taking the default ones!) */ }; 也就是说,在倒数第二行删除省略号(即使用默认参数)。
    【解决方案2】:

    首先我们编写一些机制来替换模板类型实例中的类型:

    template<class In, class Replace>
    struct replace_first_type;
    template<template<class...>class Z, class T0, class...Ts, class Replace>
    struct replace_first_type<Z<T0, Ts...>, Replace> {
      using type=Z<Replace, Ts...>;
    };
    
    template<class In, class Replace>
    using replace_first = typename replace_first_type<In,Replace>::type;
    

    replace_first&lt; Z, X &gt; 接受Z,模式将其与任何template&lt;class...&gt; 匹配,获取第一个参数,并将其替换为X

    这不适用于std::array&lt;int, 7&gt;,因为7 不是类型,但适用于std::vector&lt;int&gt;vector 实际上是带有默认参数的 &lt;T, A&gt;template&lt;class...&gt; 模式匹配包含 0 个或更多类的模板。

    然后我们应用它:

    template<typename A>
    class foo {
      template<typename B>
      using result = replace_first<B, A>;
    };
    

    现在foo&lt;int&gt;::template result&lt; std::vector&lt;double&gt; &gt;std::vector&lt;int, std::alloctor&lt;double&gt;&gt;。诚然,这是一种非常愚蠢的类型。

    如果你有一个函数:

      template<class B>
      result<B> do_something() {
        return {};
      }
    

    实际上返回该类型的值。

    我们可以通过递归替换我们替换的类型来解决上面std::allocator&lt;double&gt;的问题...

    template<class X, class Src, class Dest>
    struct subst_type {
      using type=X;
    };
    template<class X, class Src, class Dest>
    using subst_t = typename subst_type<X, Src, Dest>::type;
    template<template<class...>class Z, class...Ts, class Src, class Dest>
    struct subst_type<Z<Ts...>, Src, Dest> {
      using type=Z<subst_t<Ts, Src, Dest>...>;
    };
    template<class Src, class Dest>
    struct subst_type<Src, Src, Dest> {
      using type=Dest;
    };
    

    并先申请替换:

    template<class In, class Replace>
    struct replace_first_type;
    template<template<class...>class Z, class T0, class...Ts, class Replace>
    struct replace_first_type<Z<T0, Ts...>, Replace> {
      using type=Z<Replace, subst_t<Ts, T0, Replace>...>;
    };
    

    现在当我们foo&lt;int&gt;::result&lt;std::vector&lt;double&gt;&gt; 时,我们得到std::vector&lt;int, std::allocator&lt;int&gt;&gt; 而不是std::vector&lt;int, std::allocator&lt;double&gt;&gt;(这是一个无用的类型)。

    Live example.

    在模板的其余参数中递归地用B 替换A 可能会出现严重错误,但如前所述,如果我们不这样做,我们肯定会在使用通用模板时遇到一些可怕的问题'不要这样做。

    【讨论】:

    • 您对@Tyker 的做法有何看法?
    • @Museful 如果你不需要支持std::vector他的作品很棒。
    • @Yakk-AdamNevraumont 它确实支持 c++17 的默认模板参数
    • @Tyker 啊,我错过了 C++17 功能。它可能会重新绑定分配器,而不需要有点危险的“替换所有东西”。
    • @Yakk-AdamNevraumont 是的,分配器也需要重新绑定。所以我的方法在向量上的 c++17 中不起作用。
    猜你喜欢
    • 1970-01-01
    • 2019-11-10
    • 2013-09-13
    • 2021-10-30
    • 2020-12-04
    • 2021-03-08
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    相关资源
    最近更新 更多