可变参数模板:分组展开参数
Variadic templates: unfold arguments in groups
我有一个接受两个参数的函数:
template <typename T1, typename T2>
void foo(T1 arg1, T2 arg2)
{ std::cout << arg1 << " + " << arg2 << '\n'; }
还有一个应该成对转发其参数的可变参数:
template <typename... Args>
void bar(Args&&... args) {
static_assert(sizeof...(Args) % 2 == 0);
( foo( std::forward<Args>(args), std::forward<Args>(args) ), ... );
// ^ Sends each argument twice, not in pairs
}
我希望 bar(1,2,3,4)
调用 foo(1,2)
和 foo(3,4)
有办法吗?
您可以通过重载来完成它。
template <typename T1, typename T2>
void bar(T1&& arg1, T2&& arg2) {
foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // (until) sends (the last) two arguments to foo
}
template <typename T1, typename T2, typename... Args>
void bar(T1&& arg1, T2&& arg2, Args&&... args) {
foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // sends the 1st two arguments to foo
bar( std::forward<Args>(args)... ); // call bar with remaining elements recursively
}
请注意,在使用 0 或奇数参数调用 bar
时,使用上面的最小代码段,您将得到 无匹配函数 错误。如果你想要更清晰的static_assert
编译信息,你可以从这个snippet.
开始
使用if constexpr
的简单递归:
// print as many pairs as we can
template<class T, class U, class... Args>
void foo(T t, U u, Args&&... args)
{
std::cout << t << " + " << u << "\n";
if constexpr(sizeof...(Args) > 0 && sizeof...(Args) % 2 == 0)
foo(std::forward<Args>(args)...);
}
template<class... Args>
void bar(Args&&... args)
{
static_assert(sizeof...(Args) % 2 == 0);
foo(std::forward<Args>(args)...);
}
这样称呼它:
bar(1, 2, 3, 4);
Demo
我会说 is pretty canonical pre-C++17. Afterwards, if constexpr
允许我们将逻辑移动到我们函数的主体中,而不是使用重载技巧。
n
-ary 仿函数的 C++17 泛化:
namespace impl
{
template<std::size_t k, class Fn, class Tuple, std::size_t... js>
void unfold_nk(Fn fn, Tuple&& tuple, std::index_sequence<js...>) {
fn(std::get<k + js>(std::forward<Tuple>(tuple))...);
}
template<std::size_t n, class Fn, class Tuple, std::size_t... is>
void unfold_n(Fn fn, Tuple&& tuple, std::index_sequence<is...>) {
(unfold_nk<n * is>(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<n>{}), ...);
}
}
template<std::size_t n, class Fn, typename... Args>
void unfold(Fn fn, Args&&... args) {
static_assert(sizeof...(Args) % n == 0);
impl::unfold_n<n>(fn, std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(Args) / n>{});
}
int main() {
auto fn = [](auto... args) {
(std::cout << ... << args) << ' ';
};
unfold<2>(fn, 1, 2, 3, 4, 5, 6); // Output: 12 34 56
unfold<3>(fn, 1, 2, 3, 4, 5, 6); // Output: 123 456
}
我有一个接受两个参数的函数:
template <typename T1, typename T2>
void foo(T1 arg1, T2 arg2)
{ std::cout << arg1 << " + " << arg2 << '\n'; }
还有一个应该成对转发其参数的可变参数:
template <typename... Args>
void bar(Args&&... args) {
static_assert(sizeof...(Args) % 2 == 0);
( foo( std::forward<Args>(args), std::forward<Args>(args) ), ... );
// ^ Sends each argument twice, not in pairs
}
我希望 bar(1,2,3,4)
调用 foo(1,2)
和 foo(3,4)
有办法吗?
您可以通过重载来完成它。
template <typename T1, typename T2>
void bar(T1&& arg1, T2&& arg2) {
foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // (until) sends (the last) two arguments to foo
}
template <typename T1, typename T2, typename... Args>
void bar(T1&& arg1, T2&& arg2, Args&&... args) {
foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // sends the 1st two arguments to foo
bar( std::forward<Args>(args)... ); // call bar with remaining elements recursively
}
请注意,在使用 0 或奇数参数调用 bar
时,使用上面的最小代码段,您将得到 无匹配函数 错误。如果你想要更清晰的static_assert
编译信息,你可以从这个snippet.
使用if constexpr
的简单递归:
// print as many pairs as we can
template<class T, class U, class... Args>
void foo(T t, U u, Args&&... args)
{
std::cout << t << " + " << u << "\n";
if constexpr(sizeof...(Args) > 0 && sizeof...(Args) % 2 == 0)
foo(std::forward<Args>(args)...);
}
template<class... Args>
void bar(Args&&... args)
{
static_assert(sizeof...(Args) % 2 == 0);
foo(std::forward<Args>(args)...);
}
这样称呼它:
bar(1, 2, 3, 4);
Demo
我会说 if constexpr
允许我们将逻辑移动到我们函数的主体中,而不是使用重载技巧。
n
-ary 仿函数的 C++17 泛化:
namespace impl
{
template<std::size_t k, class Fn, class Tuple, std::size_t... js>
void unfold_nk(Fn fn, Tuple&& tuple, std::index_sequence<js...>) {
fn(std::get<k + js>(std::forward<Tuple>(tuple))...);
}
template<std::size_t n, class Fn, class Tuple, std::size_t... is>
void unfold_n(Fn fn, Tuple&& tuple, std::index_sequence<is...>) {
(unfold_nk<n * is>(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<n>{}), ...);
}
}
template<std::size_t n, class Fn, typename... Args>
void unfold(Fn fn, Args&&... args) {
static_assert(sizeof...(Args) % n == 0);
impl::unfold_n<n>(fn, std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(Args) / n>{});
}
int main() {
auto fn = [](auto... args) {
(std::cout << ... << args) << ' ';
};
unfold<2>(fn, 1, 2, 3, 4, 5, 6); // Output: 12 34 56
unfold<3>(fn, 1, 2, 3, 4, 5, 6); // Output: 123 456
}