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 {};
在任何朴素的模式匹配下,foo 具有无限的通透性。
在实践中,它具有1 或3 的通风度。
一般来说,“这个模板的空气感是什么”这个问题是错误的问题。相反,“这些类型可以传递给这个模板吗”,或者“这些类型中有多少可以传递给这个模板”是更有用的。
寻找模板的通透性就像想要从可调用对象中提取签名一样。如果你知道如何称呼一个对象,问“我可以这样称呼它吗?怎么样?”是合理的;问“告诉我怎么称呼你”几乎总是被误导。
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...>> {};
以上回答了“我可以将某些类型应用于模板”的问题。
现在,您可以将一组类型的最长前缀应用于模板:
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;
}
可以编写类似的代码,该代码采用一组类型和一个模板,并重复切掉可以传递给模板的一组类型的最长前缀。
(上面的代码肯定有错别字)。
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...>
>;
现在我们可以获取一个模板和一组类型,并通过依次将模板应用于这组类型的最长前缀来构建一组类型。
如果我们疯了,我们甚至可以进行回溯,这样如果我们的模板需要 2 或 3 个元素,而我们给它 4 个,它就不会尝试给它 3 个,然后在剩下 1 个元素时失败——相反,它可以找到每个应用程序的最长前缀,以允许类似地捆绑尾部。