9

For example, suppose I make a class like below:

template <unsigned int INPUT_SIZE>
class A{
public:
    int operator()(int input, ...){ // get INPUT_SIZE-many inputs
        // return sum;
    }
};

I want to get input as many as INPUT_SIZE, not more or less. How can I achieve that?

Also, I am using c++11, but if there is a better way in c++14 or above, I would also like to know.

user5876164
  • 471
  • 3
  • 15
  • With a for loop. – tkausl Dec 23 '18 at 04:30
  • @tkausl Can you give an example of how that would look? – Lightness Races in Orbit Dec 23 '18 at 04:34
  • 3
    Does it need to be [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae)-friendly? If not, I'd use a `static_assert` inside the body of `operator()`; if so, I'd go with Jans's answer until C++20, at which point I think you can put the `sizeof...` in a `requires` clause. – Daniel H Dec 23 '18 at 04:50
  • 1
    See also: [Number of Variadic Template Function Parameters?](https://stackoverflow.com/questions/12024304/c11-number-of-variadic-template-function-parameters) – user202729 Dec 23 '18 at 09:27

1 Answers1

14

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.

Jans
  • 11,064
  • 3
  • 37
  • 45