如何"duplicate"模板参数包扩展?

How to "duplicate" template parameter pack expansion?

本文关键字:扩展 参数 duplicate 如何 包扩展      更新时间:2023-10-16

请考虑以下代码:

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

回答以下问题的关键:

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

我能想象到的最好的是使用std::tuple_catstd::make_pairstd::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>{});
}