如何重复"模板参数包扩展?

时间:2018-02-03 18:17:55

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

考虑以下代码:

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));
}

2 个答案:

答案 0 :(得分:3)

回答问题的关键:

// 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
    );
}

答案 1 :(得分:2)

我能想象的最好的是使用std::tuple_catstd::make_pair来制作std::tuple foo()个参数。

不幸的是,我知道如何只使用辅助函数来调用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)>{});
 }

使用仅从C ++ 17开始提供的std::apply,您可以使用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>{});
 }