Reputation: 445
suppose I have a template
template<typename Bar>
Result foo(const Input& input);
and I instead want to create a template foo()
that would get many Bar
template arguments and many Input
's and put the Result
together.
I wrote a version with a container of inputs:
template<typename Bar>
Result foo(const std::vector<Input>& inputs);
template<typename Bar1, typename Bar2, typename ... Bars>
Result foo(const std::vector<Input>& inputs) {
// calls foo<Bar2, Bars ...>(shorter_inputs);
}
However, the version above cannot check at compile time if the length of the input matches the number of template arguments. Also, I want a version that would simply take the inputs, without the need for a container:
foo<Bar1, Bar2>(input1, input2);
// instead of
// foo<Bar1, Bar2>({input1, input2});
My attempts to write something like this
template<typename Bar1, typename Bar2, typename ... Bars>
Result bar(const Input& in1, const Input& in2, const Input& ... inputs)
were rejected by the compiler, because ...
only works with a parameter pack. Is it possible to write such a variadic function in C++, without resorting to va_args
?
Upvotes: 2
Views: 142
Reputation: 6144
Just use two parameter packs:
template<typename Bar, typename... Bars, typename Input, typename... Inputs>
std::string foo(const Input &input, const Inputs &... inputs)
{
std::string result(Bar::get_name());
result.append(input);
if constexpr (sizeof...(inputs) > 0)
result.append(foo<Bars...>(inputs...));
return result;
}
Upvotes: 3
Reputation: 66210
Not sure to understand what do you want...
If Result
and Input
are types and not template parameters and you want a template foo()
that receive as many Input
objects a template parameters, you can start from a custom template parameter GetFirst
to select the first template parameter in a variadic list
template <typename T0, typename ...>
struct GetFirst
{ using type = T0; };
and write foo()
as follows
template <typename ... Bars>
Result foo (typename GetFirst<Input, Bars>::type const & ... is)
{
// something with is...
return {};
}
So you have
//foo<long, long long>(Input{}, Input{}, Input{}); // compilation error
foo<int, long, long long>(Input{}, Input{}, Input{}); // compiles
//foo<int, long, long long>(Input{}, Input{}); // compilation error
If you want to manage the single is...
recursively, you can write foo()
as follows
template <typename, typename ... Bars>
Result foo (Input const & i0,
typename GetFirst<Input, Bars>::type const & ... is)
{
// do something with i0
return foo<Bars...>(is...);
}
but you need also a ground case template foo()
to terminate the recursion; I propose a foo()
that receive a template not-type parameter with a default value (to intercept the Bars...
when the list is empty)
template <int = 0>
Result foo ()
{ return {}; }
The following is a full, maybe silly, but compiling example
struct Input { };
struct Result { };
template <typename T0, typename ...>
struct GetFirst
{ using type = T0; };
// ground case
template <int = 0>
Result foo ()
{ return {}; }
// recursive case
template <typename, typename ... Bars>
Result foo (Input const & i0,
typename GetFirst<Input, Bars>::type const & ... is)
{
// do something with i0
return foo<Bars...>(is...);
}
int main()
{
//foo<long, long long>(Input{}, Input{}, Input{}); // compilation error
foo<int, long, long long>(Input{}, Input{}, Input{}); // compiles
//foo<int, long, long long>(Input{}, Input{}); // compilation error
}
Upvotes: 1