【问题标题】:Iterate over C++ variadic template迭代 C++ 可变参数模板
【发布时间】:2017-08-29 12:53:39
【问题描述】:

我有以下几点:

template<typename FIRST, typename SECOND>
Sender *createSenderChain() {
    return new FIRST(new SECOND());
}

是否可以使模板可变:

template<typename FIRST, typename ...Args>
Sender *createSenderChain() {
    return new FIRST(new SECOND(new THIRD(new ...))  <-- This is the pattern I want, 
                                                         but how should it be done 
                                                         using the args list?
}

【问题讨论】:

    标签: c++ templates variadic


    【解决方案1】:

    您可以为此使用递归

    猜测你对Sender的定义:

    struct Sender { ~Sender() {} };
    
    struct A : Sender { A(Sender* = nullptr) {} };
    struct B : Sender { B(Sender* = nullptr) {} };
    struct C : Sender { C(Sender* = nullptr) {} };
    
    
    // Base case
    template <typename T>
    Sender* createSenderChain()
    {
        return new T();
    }
    
    // Recursive case
    template <typename T1, typename T2, typename ...Ts>
    Sender* createSenderChain()
    {
        return new T1(createSenderChain<T2, Ts...>());
    }
    
    int main()
    {
        auto ptr = createSenderChain<A, B, C>();
    }
    

    (live demo)

    【讨论】:

    • 顺便说一下,比起原始的new,更喜欢std::unique_ptrstd::shared_ptr。我的演示有三个内存泄漏!
    【解决方案2】:

    您可以使用不同的模板参数调用相同的可变参数模板函数:

    template<typename FIRST, typename SECOND, typename ...Args>
    Sender* createSenderChain() {
        return new typename FIRST(createSenderChain<SECOND, Args...>());
    }
    
    template<typename FIRST>
    Sender* createSenderChain() {
        return new typename FIRST();
    }
    

    在第一个函数中,我们不仅显式声明了typename FIRST,还显式声明了typename SECOND,以避免将此实现与createSenderChain&lt;T&gt; 调用匹配,因为可变参数部分可能与空的类型列表匹配。

    【讨论】:

      【解决方案3】:

      由于您的问题没有指定使用哪个 C++ 变体,因此此解决方案使用 C++17。这意味着它可以避免递归。

      我们通过二元折叠来解决函数的组合问题。为此,我们首先需要将函数对象转换为可以通过二进制操作组合的对象:

      template<class F>
      struct compose_t {
        F f;
        template<class Lhs, class Rhs>
        auto operator*( compose_t<Lhs> lhs, compose_t<Rhs> rhs ) {
          auto r =
            [lhs = std::move(lhs).f, rhs = std::move(rhs).f](auto&&...args)
            ->decltype(auto)
            { return lhs(rhs(decltype(args)(args)...)); }
          return compose_t<decltype(r)>{ std::move(r) };
        }
        template<class...Args>
        decltype(auto) operator()(Args&&...args){
          return f(std::forward<Args>(args)...);
        }
      };
      template<class F>
      compose_t<F> compose(F f) { return {std::forward<F>(f)}; }
      

      这将创建一个与* 组合的可组合函数对象。

      接下来,我们需要一个表示“在堆上构造一个T”的对象,而不指定对象是如何完成的

      template<class T>
      auto maker() {
        return [](auto&&...args) {
          return std::make_unique<T>( decltype(args)(args)...) )
        };
      }
      

      maker 返回一个函数对象,表示在稍后提供的一组参数上调用make_unique&lt;T&gt;。我可以用原始指针做到这一点,但我拒绝。

      template<typename ...Args>
      std::unique_ptr<Sender> createSenderChain() {
        return (compose( maker<Args>() ) * ...)();
      }
      

      然后完成。请注意,我使用unique_ptr&lt;Sender&gt;s 而不是Sender*s,因为我拒绝提供您不应该使用的垃圾代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-20
        • 1970-01-01
        • 1970-01-01
        • 2015-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多