My initial goal is to disambiguate the following:
template<typename... T, typename... U>
void foo(){}
In that case, foo< <a_sequence_of_types> >()
always results in T = <a_sequence_of_types>
and U = <empty_parameter_pack>
.
A classic solution is to pack T...
and U...
in std::tuple<T...>
and std::tuple<U...>
respectively and use these types as default arguments in foo.
template<typename... T, typename... U>
void foo(std::tuple<T...>, std::tuple<U...>){}
Problems arise if any of the involved template parameters is not default constructible as it is becomes impossible to bind the dummy parameters of foo in that case.
Then I started looking for a standard, idiomatic template parameter pack wrapper without luck. A false hope was std::index_sequence_for
that does not enable template parameter pack deduction as it is only a template alias for std::integer_sequence
that erases its template type parameter pack.
I ended up using my own wrapper
template<typename... T>
struct TypePack{};
template<typename... T, typename... U>
void foo(TypePack<T...>, TypePack<U...>){}
which is trivially constexpr default constructible and works like a charm in place of std::tuple. But it lacks the clarity that standard types provide when used in an idiomatic way. Is there any such more idiomatic way?
NOTE: This is part of the implementation of my code base and is not visible from the end user. Please do not tackle that question under the angle of API design. I am more interested in self-documenting code at that point.
CodePudding user response:
Problems arise if any of the involved template parameters is not default constructible as it is becomes impossible to bind the dummy parameters of foo in that case.
You can pass std::type_identity<T>
instead of T
to tuple to pass type:
foo(std::tuple<std::type_identity<int&>,
std::type_identity<const float&>>{},
std::tuple<std::type_identity<void>>{});
Another possibility is to break foo
in 2 parts:
template <typename... Ts>
struct foo_t
{
template <typename... Us>
static void foo() {/*...*/}
};
with usage similar to
foo_t<int&, const float&>::foo<void>();