For example, suppose I make a class like below:
template
class A{
public:
int operator()(int input, ...){ // get INPUT_SI
Live demo 1
template <class T, auto> using always_t = T;
template <class T, class Arity>
struct A_impl;
template <class T, std::size_t... Is>
struct A_impl<T, std::index_sequence<Is...>>
{
int operator ()(always_t<T, Is>...)
{
return 0;
}
};
template <std::size_t N>
struct A : A_impl<int, std::make_index_sequence<N>>
{ };
A<2>{}(1, 2); // fine
A<2>{}(1, 2, 3); // fail
and this is a version that allows you to compute the sum of the parameters:
Live demo 2
template <class T, auto> using always_t = T;
template <class T, class Arity>
struct A_impl;
template <class T, std::size_t... Is>
struct A_impl<T, std::index_sequence<Is...>>
{
constexpr int operator ()(std::tuple<always_t<T, Is>...>&& t) {
auto adder = [](auto... ts) {
return (0 + ... + ts);
};
return std::apply(adder, std::move(t));
}
};
template <std::size_t N>
struct A : A_impl<int, std::make_index_sequence<N>>{
};
constexpr int sum = A<3>{}({1, 4, 5});
static_assert(sum == 10);
The trick is to use a parameter pack with length N
so that we can use it to expand as N
times a specific type into the parameter list of A_impl::operator()
.
A parameter pack can expand into N
repetition of the pattern that (usually) precede ...
Consider a function like:
template<class... T>
void foo(T...);
T...
indicate in simple terms that it can be replaced by successive types into the parameter list of foo
, one possible expansion could be foo(int, int, double, char)
, also notice that what preside ...
is an identifier that comes from class... T
.
Returning to the code, we need to generate a parameter pack, we did that through std::make_index_sequence<N>
, that generate the sequence 0..(N-1)
which is captured by std::size_t... Is
, then we use this pack to expand the pattern always_t<T, Is>
that is just an alias to T=int
, this end up repeating T=int
as many times as elements Is
contains.
Note: ellipsis parameter ...
is not the same as parameter pack.