Home > front end >  Idiomatic template parameter pack wrapper for template parameter pack deduction disambiguation
Idiomatic template parameter pack wrapper for template parameter pack deduction disambiguation

Time:10-03

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>();
  • Related