Iterate on variadic arguments is the simplest part: you tagged C++17 so you can use template folding, as suggested by JeJo, or other ways (recursion, initialization of an unused array).
More complicated is impose that all the arguments are exactly of the same type.
Obviously you can use SFINAE to impose that the deduced type are of the same type, but if you pass arguments of the different types, by example
foo(1l, 2l, 3l, 4); // long, long, long, int
when an argument is convertible to the type of the others, the code doesn't compile.
If you accept to pass through an additional function and that your function is a method of a template struct, you can start with an using
that select the type from a couple type/index
template <typename T, std::size_t>
using get_type = T;
you can write the template struct as follows
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
Observe that the arguments following str
in the operator()
are all of type T
, where T
is the first template argument of the struct.
The additional function is
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
You can call foo()
as follows
foo<int>("string", 1, 2, 3, 4l);
Observe that a long
value (4l
) is accepted because is converted to int
.
You can also directly call the bar::operator()
, if you prefer
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
but you have to explicit the second template argument so there is some redundancies.
The following is a full compiling example
#include <string>
#include <utility>
#include <iostream>
template <typename T, std::size_t>
using get_type = T;
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
int main ()
{
foo<int>("string", 1, 2, 3, 4l); // a long value is converted to int
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
}