The main problem in question is that I want to write a function that takes two variable-length sets of arguments.
The abstraction I decided to go for is to emulate the following call syntax:
f({a,b,c},x,y);
If a
, b
, and c
all have the same type, this can be made to work with
template <typename X, typename... A>
void f(std::initializer_list<X> xs, A&&... as) { . . . }
But a similar function definition with std::tuple
instead of the std::initializer_list
does not allow for my desired syntax.
template <typename... X, typename... A>
void f(const std::tuple<X...>& xs, A&&... as) { . . . }
Is there some trick I can use to allow the types in the first group to be heterogeneous?
Addendum: In general, I do not apriori know the size of X...
.
Addendum 2: Does anyone know the technical reason why a braced initializer works for std::tuple t{a,b,c}
, but not in the template argument context? Is it because in my example xs
is not expanded by X...
?
No, it is currently impossible with the syntax you want. The initializer list can only be deduced to an array or a std::initializer_list
, both of homogeneous types.
This is a special rule. An initializer list has no type and simply cannot be deduced to anything else. If the function parameter is not an array or a std::initializer_list
, then the parameter for which the initializer list was given becomes a non-deduced context and template argument deduction will ignore it.
In particular class template argument deduction cannot be done in a function parameter to determine the type from a braced initializer list as it can be done in a variable definition since C++17. Such a feature was not added to the language.
In principle you could allow a finite number of different types by using e.g. an array of std::variant
as parameter, but that would be resolved to the type only at runtime and is unlikely to be what you want.
You need to either add a type name or function call to the initializer list braces as in
f(std::forward_as_tuple(a, b, c), x, y)
or you can use a tag type instead to indicate the end of the first pack in a flat argument list:
f(a, b, c, tag, x, y)
The tag can then be detected with some template work.