I need to define a function with template parameter pack with C++14.
The caller of the function makes sure that the size of Args...
must be even, such as 2, 4, 6... And my function will pass them two by two to two functions.
template<typename F, typename F2, typename... Args>
void func(F f, F2 f2, Args&&... params) {
using params_t = std::tuple<Args...>;
auto tp = std::make_tuple(params...);
for (std::size_t i = 0; i < sizeof...(Args); ++i) {
f(std::get<i>(tp));
f2(std::get<++i>(tp));
}
}
// caller
func(f1, f2, "abc", 3, "def", "ppp");
This won't work because i
is not a constant expression.
What could I do? Is it the right and the only way to iterate over a parameter pack with std::tuple
?
To use a std::tuple
to iterate over a parameter pack, you would usually use a std::index_sequence
to introduce a new pack of indices, and use a fold expression to do the actual iteration. Something like this:
template<typename F, typename F2, typename... Args, std::size_t... I>
void func_impl(F& f, F2& f2, std::tuple<Args...> params, std::index_sequence<I...>) {
// This would be a fold expression over a comma in C++17:
/*
(([&]{}(
f(std::get<I*2>(params));
f2(std::get<I*2+1>(params));
)), ...);
*/
// C++14 version
using consume = int[];
(void) consume{ 0, [&]{
f(std::get<I*2>(params));
f2(std::get<I*2+1>(params));
return 0;
}()... };
}
template<typename F, typename F2, typename... Args>
void func(F f, F2 f2, Args&&... args) {
static_assert(sizeof...(args) % 2 == 0, "Must pass a multiple of 2 args to func");
func_impl(
f, f2,
std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(args) / 2>{}
);
}
But you can also iterate with recursion, which might be easier in your case:
template<typename F, typename F2>
void func(F&& f, F2&& f2) {
// base case: do nothing
}
template<typename F, typename F2, typename Arg1, typename Arg2, typename... Args>
void func(F&& f, F2&& f2, Arg1&& arg1, Arg2&& arg2, Args&&... args) {
// recursive case
f(arg1);
f2(arg2);
func(std::forward<F>(f), std::forward<F2>(f2), std::forward<Args>(args)...);
}