C++ argument pack recursion with two arguments

Viewed 108

I have a template class that uses two types:

template<typename T1,typename T2> class Foo { ... };

I need to write a function that takes any number of Foo variables:

template <typename T1, typename T2, typename... Others> size_t getSize(Foo<T1,T2> *f, Foo<Others>*... o) { ... };

If I implement the class Foo with only one template parameter, it works well. But with two (or more) parameters, the compiler complains that Foo<Others> requires two args.

Is is possible to achieve argument pack forwarding when the class Foo has multiple template parameters ?

2 Answers

Your mistake is Foo<Others>*... o argument. Unpacking that argument for a template pack A, B, C, D, E would yield you with as many o arguments: Foo<A>, Foo<B>, Foo<C>, ....

In my opinion, it would be simpler if you just declare your arguments as Other and let the recursion fail if they won't match with any Foo instantiation later on:

template <typename T1, typename T2, typename... Others> size_t getSize(Foo<T1,T2> *f, Others... *o) { ... };

Here, each Others type in the pack will be deduced to the type you pass. If you call getSize recursively with a reduced number of arguments, they will all eventually be matched with a Foo<T1,T2> argument:

return f->size() 
     + getSize(std::forward<Others>(o)...); // the first argument in the pack will
                                            // be Foo<T3,T4> type 
                                            // or the compilation will fail

You could also add a type trait to do the check directly:

template <class T> struct IsFoo : std::false_type {};
template <class T, class U> struct IsFoo<Foo<T,U>> : std::true_type {};

template </* ... */>
std::enable_if_t<std::conjunction<IsFoo<Others>::value...>::value, size_t> getSize(/* ... */)

What about

template <typename ... Ts1, typename ... Ts2>
std::size_t get_size (Foo<Ts1, Ts2> * ... fs)
 { /* ... */ }

?

Or, maybe,

template <typename T1, typename T2, typename ... Us1, typename ... Us2>
std::size_t get_size (Foo<T1, T2> * f, Foo<Us1, Us2> * ... fs)
 { /* ... */ }

if you want a first Foo managed differently.

Related