作为第一个参数的可变参数模板
Variadic template as first arguments
我想编写一个通用模板函数,它接受和调用许多不同的函数并执行额外的设置和拆卸操作。函数签名在第一个参数上有所不同,例如:
void foo(int i, void* self, (*callback)(void*, int i));
void bar(bool s, bool r, void* self, (*callback)(void*, int i));
我也想挑出self
论点和callback
论点,但让前面的论点有所不同。我知道由于解包的工作方式,可变模板很难做到这一点;有什么解决办法吗?
如果我对你的问题的理解正确,你的问题是以任何顺序使用可变参数模板作为参数,第一个除外。好吧,谢天谢地,所有这些都是有效的:
template<class T, class ...Args> void call1(int y, T first, Args ... args, int x);
template<class T, class ...Args> void call2(int y, int x, T first, Args ... args);
template<class T, class ...Args> void call3(Args ... args, T first, int y, int x);
Edit:
Automatic template deduction will only work for call2
Thanks to @Human-Compiler
当然,可变参数模板是使用递归处理的,因此,为了正确处理它们,如果没有提供 first
参数,您还需要一个函数来调用:
void call1(int y, int x);
一个例子,它只会盲目调用前 x 个函数:
void print()
{
std::cout << "print";
}
void call(int x)
{
return;
}
template<class T, class ...Args> void call(int x, T f, Args ... args)
{
if(x <= 0) return;
f();
call(--x, args...);
}
int main()
{
call(3, print, print, print);
}
根据您的用例,您可以接受 callback
和 self
作为参数包之前的第一个参数,然后按正确的顺序合并这两个参数:
#include <tuple>
#include <functional>
template <typename F, typename... Ts>
void wrap(F f, void* self, void (*callback)(void*, int), Ts&&... ts)
{
// change self and callback
std::apply(f, std::tuple_cat(std::forward_as_tuple(std::forward<Ts>(ts)...),
std::make_tuple(self, callback)));
}
或者根据参数在参数包中的位置提取参数:
#include <tuple>
#include <functional>
template <typename F, typename... Ts>
void wrap(F f, Ts&&... ts)
{
constexpr int N = sizeof...(Ts);
auto args = std::tie(ts...);
void*& self = std::get<N-2>(args);
void (*&callback)(void*, int) = std::get<N-1>(args);
// change self and callback
std::apply(f, args);
}
有了保留值类别,上面就变成了:
#include <tuple>
#include <cstddef>
#include <utility>
template <typename Tuple, std::size_t... Is>
auto take(Tuple&& t, std::index_sequence<Is...>)
{
return std::forward_as_tuple(std::get<Is>(std::forward<Tuple>(t))...);
}
template <typename F, typename... Ts>
void wrap(F f, Ts&&... ts)
{
constexpr int N = sizeof...(Ts);
auto args = std::tie(ts...);
void* self = std::get<N-2>(args);
void (*callback)(void*, int) = std::get<N-1>(args);
auto firsts = take(std::forward_as_tuple(std::forward<Ts>(ts)...),
std::make_index_sequence<N-2>{});
// change self and callback
std::apply(f, std::tuple_cat(std::move(firsts), std::make_tuple(self, callback)));
}
我想编写一个通用模板函数,它接受和调用许多不同的函数并执行额外的设置和拆卸操作。函数签名在第一个参数上有所不同,例如:
void foo(int i, void* self, (*callback)(void*, int i));
void bar(bool s, bool r, void* self, (*callback)(void*, int i));
我也想挑出self
论点和callback
论点,但让前面的论点有所不同。我知道由于解包的工作方式,可变模板很难做到这一点;有什么解决办法吗?
如果我对你的问题的理解正确,你的问题是以任何顺序使用可变参数模板作为参数,第一个除外。好吧,谢天谢地,所有这些都是有效的:
template<class T, class ...Args> void call1(int y, T first, Args ... args, int x);
template<class T, class ...Args> void call2(int y, int x, T first, Args ... args);
template<class T, class ...Args> void call3(Args ... args, T first, int y, int x);
Edit:
Automatic template deduction will only work for call2
Thanks to @Human-Compiler
当然,可变参数模板是使用递归处理的,因此,为了正确处理它们,如果没有提供 first
参数,您还需要一个函数来调用:
void call1(int y, int x);
一个例子,它只会盲目调用前 x 个函数:
void print()
{
std::cout << "print";
}
void call(int x)
{
return;
}
template<class T, class ...Args> void call(int x, T f, Args ... args)
{
if(x <= 0) return;
f();
call(--x, args...);
}
int main()
{
call(3, print, print, print);
}
根据您的用例,您可以接受 callback
和 self
作为参数包之前的第一个参数,然后按正确的顺序合并这两个参数:
#include <tuple>
#include <functional>
template <typename F, typename... Ts>
void wrap(F f, void* self, void (*callback)(void*, int), Ts&&... ts)
{
// change self and callback
std::apply(f, std::tuple_cat(std::forward_as_tuple(std::forward<Ts>(ts)...),
std::make_tuple(self, callback)));
}
或者根据参数在参数包中的位置提取参数:
#include <tuple>
#include <functional>
template <typename F, typename... Ts>
void wrap(F f, Ts&&... ts)
{
constexpr int N = sizeof...(Ts);
auto args = std::tie(ts...);
void*& self = std::get<N-2>(args);
void (*&callback)(void*, int) = std::get<N-1>(args);
// change self and callback
std::apply(f, args);
}
有了保留值类别,上面就变成了:
#include <tuple>
#include <cstddef>
#include <utility>
template <typename Tuple, std::size_t... Is>
auto take(Tuple&& t, std::index_sequence<Is...>)
{
return std::forward_as_tuple(std::get<Is>(std::forward<Tuple>(t))...);
}
template <typename F, typename... Ts>
void wrap(F f, Ts&&... ts)
{
constexpr int N = sizeof...(Ts);
auto args = std::tie(ts...);
void* self = std::get<N-2>(args);
void (*callback)(void*, int) = std::get<N-1>(args);
auto firsts = take(std::forward_as_tuple(std::forward<Ts>(ts)...),
std::make_index_sequence<N-2>{});
// change self and callback
std::apply(f, std::tuple_cat(std::move(firsts), std::make_tuple(self, callback)));
}