【问题标题】:How to "duplicate" template parameter pack expansion?如何“复制”模板参数包扩展?
【发布时间】:2018-02-06 04:01:14
【问题描述】:

考虑这段代码:

template < size_t... Indices >
void something(std::index_sequence<Indices...>)
{
  // how to obtain the following call where N is sizeof...(Indices)?
  // foo(f(0),g(0),f(1),g(1),...,f(N-1),g(N-1));
}

【问题讨论】:

    标签: c++ templates variadic-templates template-meta-programming c++17


    【解决方案1】:

    回答如下问题的关键:

    // how to obtain the following call where N is sizeof...(Indices)?
    // foo(f(0),g(0),f(1),g(1),...,f(N-1),g(N-1));
    

    是制定如何实际生成这样的包扩展。我们有 2N 参数,它们想:

    +=====+======+
    | idx | expr |
    +-----+------+
    |  0  | f(0) |
    |  1  | g(0) |
    |  2  | f(1) |
    |  3  | g(1) |
    |   ....
    +=====+======+
    

    在 C++17 中,我们可以用if constexpr 编写这样的表达式:

    template <size_t I>
    auto expr() {
        if constexpr (I % 2 == 0) { 
            // even indices are fs
            return f(I / 2);
        } else {
            // odds are gs
            return g(I / 2);
        }
    }
    

    它甚至可以是一个以 integral_constant 为参数的 lambda。所以我们真的只需要将我们的N 参数变成2N 参数,这只是添加更多索引的问题:

    template <auto V>
    using constant = std::integral_constant<decltype(V), V>;
    
    template < size_t... Indices >
    void something(std::index_sequence<Indices...>) {
        auto expr = [](auto I) {
            if constexpr (I % 2 == 0) {
                return f(I / 2);
            } else {
                return g(I / 2);
            }
        }
    
        return foo(
            expr(constant<Indices>{})...,                     // 0, ..., N-1
            expr(constant<Indices + sizeof...(Indices)>{})... // N, ..., 2N-1
        );
    }
    

    【讨论】:

      【解决方案2】:

      我能想到的最好的方法是使用std::tuple_catstd::make_pair 来制作std::tuplefoo() 参数。

      不幸的是,我只知道如何使用辅助函数来调用foo()

      template <typename T, std::size_t... I>
      void somethingH (T const & t, std::index_sequence<I...> const &)
       { foo(std::get<I>(t)...); }
      
      template <std::size_t... I>
      void something (std::index_sequence<I...> const &)
       {
         somethingH(std::tuple_cat(std::make_pair(f(I), g(I))...),
                    std::make_index_sequence<(sizeof...(I) << 1)>{});
       }
      

      使用 std::apply,仅从 C++17 开始可用,您可以使用 lambda 函数来选择正确的 foo(正如 SirGuy 建议的那样;谢谢!)并避免使用辅助函数

      template <std::size_t... I>
      void something (std::index_sequence<I...> const &)
       {
         std::apply([](auto && ... as)
                       { return foo(std::forward<decltype(as)>(as)...); },
                    std::tuple_cat(std::make_pair(f(I), g(I))...));
       }
      

      以下是完整的 C++17 工作示例

      #include <iostream>
      #include <utility>
      #include <tuple>
      
      int f (std::size_t n)
       { return n; }
      
      int g (std::size_t n)
       { return -n; }
      
      template <typename ... Args>
      void foo (Args ... as)
       { (std::cout << ... << as) << std::endl; }
      
      template <std::size_t... I>
      void something (std::index_sequence<I...> const &)
       {
         std::apply([](auto && ... as)
                       { return foo(std::forward<decltype(as)>(as)...); },
                    std::tuple_cat(std::make_pair(f(I), g(I))...));
       }
      
      int main()
       {
         something(std::make_index_sequence<7U>{});
       }
      

      【讨论】:

      • 对于std::apply 版本,为什么不直接创建一个lambda 而不是一个名为getCorrectFoo 的函数呢? std::apply([](auto &amp;&amp; ... args) { return foo(std::forward&lt;decltype(args)&gt;(args)...);}, t);
      • @SirGuy - 因为我不习惯像你一样使用 lambda 函数 :( 。非常好的改进;谢谢!
      猜你喜欢
      • 2014-10-30
      • 1970-01-01
      • 2015-05-21
      • 1970-01-01
      • 2021-12-28
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多