Reputation: 417
I'm trying to use a variadic template function where the parameter pack is not the last parameter in the list. Note that there are two recursive calls--one dropping a parameter in front of the pack, the other call dropping a parameter after the pack.
There's no point in using ... if the call site for Blender can't be clean. I could just expand several overloads of Blender myself in that case. I'd really rather not resort to this. I hope I'm just missing something.
int Blender( double t, int i)
{
return i;
}
template <typename ...Args>
int Blender( double t, int first, Args... more, int last)
{
return (1-t)*Blender(t, first, more...) + t*Blender(t, more..., last);
}
static void tryit()
{
Blender(.5, 23, 42, 89); //doesn't compile
}
Upvotes: 2
Views: 192
Reputation: 283684
This is actually really easy once you realize that the limitation is in the type pack deduction and not in the function call. We just need a way to use the type pack we can deduce and pass it explicitly avoiding deduction at the function that needs to pull off the last parameter:
int Blender( double t, int i)
{
return i;
}
template <typename ...Args>
int Blender( double t, int first, Args... more);
template <typename ...Args>
int BlenderWithoutLast( double t, Args... more, int last)
{
return Blender(t, more...);
}
template <typename ...Args>
int Blender( double t, int first, Args... more)
{
return (1-t)*BlenderWithoutLast<Args...>(t, first, more...) + t*Blender(t, more...);
// all the magic happens here ^^^^^
}
and now your test case compiles and runs
#include <iostream>
int main()
{
std::cout << Blender(.5, 23, 42, 89);
}
For me this works with clang and --std=c++11
Upvotes: -1
Reputation: 66200
I propose another solution, really different from the code in the question, that I think is a lot more efficient (creation on only one std::array
; blender obtained by template index sequence)
#include <array>
#include <utility>
#include <iostream>
template <typename T, std::size_t I0>
int blenderH (double t, T const & arr, std::index_sequence<I0> const &)
{ return arr[I0]; }
template <typename T, std::size_t I0, std::size_t ... Is>
auto blenderH (double t, T const & arr,
std::index_sequence<I0, Is...> const &)
-> std::enable_if_t<0U != sizeof...(Is), int>
{ return (1-t) * blenderH(t, arr, std::index_sequence<(Is-1U)...>{})
+ t * blenderH(t, arr, std::index_sequence<Is...>{}); }
template <typename ... Args>
int blender (double t, Args ... as)
{
static constexpr auto size = sizeof...(Args);
return blenderH(t, std::array<int, size>{ { as... } },
std::make_index_sequence<size>{});
}
int main()
{ std::cout << blender(.3, 23, 42, 89) << std::endl; }
Unfortunately also this solution works (std::index_sequence
and std::make_index_sequence
) starting from C++14.
-- EDIT --
Caleth say.
Some explanation of what is going on here would help. I am confused by the lack of
I0
in the body of the second overload.
I try an explanation.
Suppose is called the recursive version of BlenderH()
(the second overload) with a list of index in the std::index_sequence
value. Say 5, 6, 7 and 8; so I0
is 5
and Is...
is 6, 7, 8
.
We have to recursive call blenderH()
with indexes 5, 6, 7
first and with 6, 7, 8
next.
We can avoid to use I0
(5
) because
5, 6, 7
is obtained from 6, 7, 8
reducing by 1 every value (so the std::index_sequence<(Is-1U)...>
for the first recursive call)
and 6, 7, 8
is Is...
without modifications (so the std::index_sequence<Is...>
in the second one.
From the practical point of view, I0
is declared only to be discarded; ther is no need to use it.
Upvotes: 0
Reputation: 275405
template<std::size_t I>
using count = std::integral_constant<std::size_t, I>;
namespace details {
template<class...Args, std::size_t N,
typename std::enable_if<sizeof...(Args)==N, bool>::type = true
>
int Blender( count<N> drop, double t, int i, Args...args ) {
return i;
}
template<class...Args, std::size_t N,
typename std::enable_if<sizeof...(Args)!=N, bool>::type = true
>
int Blender( count<N> drop, double t, int i, Args...args ) {
return (1-t)*Blender( count<N+1>{}, t, i, args... ) + t*Blender( count<N>{}, t, args... );
}
}
template <typename ...Args>
int Blender( double t, int first, Args... more)
{
return details::Blender( count<0>{}, first, more... );
}
static void tryit()
{
Blender(.5, 23, 42, 89); //doesn't compile
}
here count<N>
counts the number of arguments at the end to ignore.
The two details
overload cover the case where N
is equal to the number of arguments in the pack (and hence we have 1 argument left), and when it is not. They are dispatched to using SFINAE.
Upvotes: -1