template<class...> struct foo; template<class X> struct foo<X>:std::true_type {}; template<class X, class Y, class Z> struct foo<X,Y,Z>:std::false_type {};
under any naive pattern matching, foo has endless airiness.
In practice, he has air either 1 or 3 .
In general, the question “what is the airflow of this template” is a wrong question. Rather, “whether these types can be passed to this pattern,” or “how many of these types can be passed to this pattern” is more useful.
Look for the availability of the template, as if you want to extract a signature from the called object. If you know how you are going to name the object, asking: "Can you call it that? How about this?" reasonably; asking "tell me how to call you" is almost always a mistake.
template<class...>struct types{using type=types;}; template<class types>struct types_length; template<class...Ts>struct types_length<types<Ts...>>: std::integral_constant<size_t, sizeof...(Ts)> {}; template<class...>struct voider{using type=void;}; template<class...Ts>using void_t=typename voider<Ts...>::type; namespace details { template<template<class...>class Z, class types, class=void> struct can_apply : std::false_type {}; template<template<class...>class Z, class...Ts> struct can_apply<Z,types<Ts...>,void_t<Z<Ts...>>>: std::true_type {}; }; template<template<class...>class Z, class...Ts> struct can_apply : details::can_apply<Z,types<Ts...>> {};
the above answers the question "can I apply some types to the template".
Now, the longest type set prefix you can apply to a template:
template<class T>struct tag{using type=T;}; namespace details { template<class types, class=types<>> struct pop_back {}; template<class T0, class...rhs> struct pop_back<types<T0>, types<rhs...>>:types<rhs...> {}; template<class T0, class...Ts, class...rhs> struct pop_back<types<T0, Ts...>, types<rhs...>>: pop_back<types<T0,Ts...>,types<rhs...,T0>> {}; template<class types> using pop_back_t = typename pop_back<types>::type; } template<class types> using pop_back = details::pop_back_t<types>; namespace details { template<template<class...>class Z, class types, class=void> struct longest_prefix {}; template<template<class...>class Z, class...Ts> struct longest_prefix< Z,types<Ts...>, std::enable_if_t<can_apply<Z,Ts...>> >: types<Ts...> {}; template<template<class...>class Z,class T0, class...Ts> struct longest_prefix< Z,types<T0, Ts...>, std::enable_if_t<!can_apply<Z, T0, Ts...>> >: longest_prefix<Z,pop_back_t<types<T0,Ts...>>> {}; } template<template<class...>class Z, class...Ts> using longest_prefix = typename details::longest_prefix<Z, types<Ts...>>::type; namespace details { template<class types> struct pop_front; template<> struct pop_front<types<>> {}; template<class T0, class...Ts> struct pop_front<types<T0,Ts...>>:types<Ts...>{}; template<class types> using pop_front_t=typename pop_front<types>::type; }
similar code that takes a set of types and a template, and can repeatedly cut out a long prefix of a set of types that can be passed to the template.
(The above code, of course, contains typos).
template<class types> using pop_front = details::pop_front_t<types>; template<size_t n, template<class...>class Z, class T> struct repeat : repeat< n-1, Z, Z<T> > {}; template<template<class...>class Z, class T> struct repeat<0,Z,T> : tag<T> {}; template<size_t n, template<class...>class Z, class T> using repeat_t = typename repeat<n,Z,T>::type; template<template<class...>class Z, class types> using longest_prefix_tail = repeat_t< types_length<longest_prefix<Z,Ts...>>{}, pop_front, types<Ts...> >;
Now we can take a template and a bunch of types and build a set of types obtained by applying the template to the longest prefix of a bunch of types in turn.
If we were crazy, we could even roll back, so if our template accepts 2 or 3 elements and we feed it 4, he would not try to feed it 3, and then could not leave one element on the left - instead Of this, he could find the longest prefix of each application, which allows you to tie the tail in the same way.