使用模板包进行扩展
Expanding with pack of templates
定义 template_pack
,如下例所示。鉴于
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
然后
template_pack<std::tuple<char, bool, double>, A, B, C>::type
即将
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
即始终在元组中从左到右读取,以获得足够的类型以适合每个模板。
到目前为止,这是我的解决方案。但它只适用于最多 3 种类型的模板,我看不出如何推广到任意数量类型的模板:
#include <type_traits>
#include <tuple>
template <template <typename...> class Template, typename... Ts> struct use_template;
template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
using type = Template<A>;
};
template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
using type = Template<A,B>;
};
template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
using type = Template<A,B,C>;
};
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<typename use_template<Templates, Ts...>::type...>;
};
// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
int main() {
static_assert (std::is_same<
template_pack<std::tuple<char, bool, double>, A, B, C>::type,
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
>::value, "");
}
如何概括以上内容?有可能吗?
我不会全部写出来,但我会做一个演练。
template<template<class...>class Z>
struct z_template{
template<class...Ts>using result=Z<Ts...>;
};
这让您可以像操作类型一样操作模板。
template<template<class...>class Z, class...Ts>
using can_apply = /* ... */;
这提出了问题 "is Z<Ts...>
valid? If so, return true_type
"。
template<class...>struct types{using type=types;};
这是一个轻量级的模板包。
template<template<class...>class Z, class types>
using apply = /* ... */;
这需要一个模板 Z
、一个 types<Ts...>
和 returns Z<Ts...>
。
template<class Z, class types>
using z_apply = apply<typename Z::template result, types>;
这是用 z_template
参数而不是 template
参数来实现的。
尽可能多地处理类型可以使多种元编程更容易。
接下来我们写
template<class types>using pop_front = // ...
template<class types>using reverse = // ...
template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
删除types
前后的元素。
template<class Result>
struct z_constant {
template<class...Ts>using result=Result;
};
这会找到可以有效传递给 Z::template result<???>
的 types
的最长前缀。一些避免传递太短序列的工作已经完成,以防它们失败 "late":
template<class Z, class types>
struct z_find_longest_prefix :
std::conditional_t<
can_apply< z_apply, Z, types >{},
z_constant<types>,
z_template<longest_prefix>
>::template result<Z, pop_back<types>>
{};
template<class Z, class types>
using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
struct empty_t {};
template<class Z>
struct z_find_longest_prefix<Z,types<>>:
std::conditional_t<
can_apply< Z::template result >,
types<>,
empty_t
>
{};
find_longest_prefix
如果没有有效的前缀将编译失败。
template<class Z, class types>
using z_apply_longest_prefix_t =
z_apply< Z, z_find_longest_prefix_t<Z, types> >;
template<class src, template<class...>class... Zs>
struct use_template;
template<class src, template<class...>class... Zs>
using use_template_t = typename use_template<src, Zs...>::type;
template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
struct use_template< Z0<Ts...>, Zs... > {
using type=Z0<
z_apply_longest_prefix_t<
z_template<Zs>, types<Ts...>
>...
>;
};
以及最多拼写错误和类似问题,已完成。
可以清理很多。可能不需要整个 z_
子系统。每当进行严肃的元编程时,我都倾向于 运行 它,因为突然之间,我编写的适用于类型的任何元函数现在都可以用在模板上了。
reverse
需要一些工作才能高效完成。
can_apply
在 可用。
pop_front
是微不足道的。
apply
应该很容易。
这是"find shortest prefix"。
实用工具:
// pack that is easy to append to
template<class... Ts>
struct pack{
template<class... T1s>
using append = pack<Ts..., T1s...>;
};
template<class...> using void_t = void;
肉:
// find the shortest proper prefix Ts... of [T, Rest...]
// such that A<Ts...> is well-formed, and return it.
template<template<class...> class A, // template we are going to look at
class AlwaysVoid, // for void_t
class Current, // pack containing the types we are testing next
class T, // next type to add to pack if test fails
class... Rest> // remaining types
struct find_viable
: find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
// picked if A<Ts...> is well-formed
template<template<class...> class A, class... Ts, class T, class...Rest>
struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
using type = A<Ts...>;
};
// Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
// the extra void at the end is slightly hackish - find_viable only checks
// proper prefixes, so we add an extra dummy type at the end.
template<template<class...> class A, class... Ts>
using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
然后使用它:
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts,
template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<find_viable_t<Templates, Ts...>...>;
};
这看起来很有趣,所以我尝试了一下;我并不是说这在任何方面都比现有答案更好,但也许有些人会发现它更清楚:
namespace detail {
template<typename...>
using void_t = void;
template<template<typename...> class, typename, typename, typename EnableT = void>
struct fill_template;
template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
struct fill_template<
TemplateT, TupleT, std::index_sequence<Is...>,
void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
>
{
using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
};
template<
template<typename...> class TemplateT, typename TupleT,
std::size_t I, std::size_t N
>
struct fill_template_dispatcher
{
template<typename T, typename = typename T::type>
static constexpr std::true_type test(int) { return {}; }
template<typename T>
static constexpr std::false_type test(long) { return {}; }
using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
using type = typename std::conditional_t<
test<candidate_t>(0),
candidate_t,
fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
>::type;
};
template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
{
static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
};
} // namespace detail
template<typename TupleT, template<typename...> class... TemplateTs>
struct template_pack
{
static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
using type = std::tuple<typename detail::fill_template_dispatcher<
TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
>::type...>;
};
template<typename TupleT, template<typename...> class... TemplateTs>
using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
请注意,对于可变 class 模板,这将传递尽可能少的类型(参见测试中的 D
);对于尽可能以 many 类型传递的实现,请参阅代码的变体 here(在实现中仅更改了两行)。
定义 template_pack
,如下例所示。鉴于
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
然后
template_pack<std::tuple<char, bool, double>, A, B, C>::type
即将
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
即始终在元组中从左到右读取,以获得足够的类型以适合每个模板。
到目前为止,这是我的解决方案。但它只适用于最多 3 种类型的模板,我看不出如何推广到任意数量类型的模板:
#include <type_traits>
#include <tuple>
template <template <typename...> class Template, typename... Ts> struct use_template;
template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
using type = Template<A>;
};
template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
using type = Template<A,B>;
};
template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
using type = Template<A,B,C>;
};
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<typename use_template<Templates, Ts...>::type...>;
};
// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
int main() {
static_assert (std::is_same<
template_pack<std::tuple<char, bool, double>, A, B, C>::type,
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
>::value, "");
}
如何概括以上内容?有可能吗?
我不会全部写出来,但我会做一个演练。
template<template<class...>class Z>
struct z_template{
template<class...Ts>using result=Z<Ts...>;
};
这让您可以像操作类型一样操作模板。
template<template<class...>class Z, class...Ts>
using can_apply = /* ... */;
这提出了问题 "is Z<Ts...>
valid? If so, return true_type
"。
template<class...>struct types{using type=types;};
这是一个轻量级的模板包。
template<template<class...>class Z, class types>
using apply = /* ... */;
这需要一个模板 Z
、一个 types<Ts...>
和 returns Z<Ts...>
。
template<class Z, class types>
using z_apply = apply<typename Z::template result, types>;
这是用 z_template
参数而不是 template
参数来实现的。
尽可能多地处理类型可以使多种元编程更容易。
接下来我们写
template<class types>using pop_front = // ...
template<class types>using reverse = // ...
template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
删除types
前后的元素。
template<class Result>
struct z_constant {
template<class...Ts>using result=Result;
};
这会找到可以有效传递给 Z::template result<???>
的 types
的最长前缀。一些避免传递太短序列的工作已经完成,以防它们失败 "late":
template<class Z, class types>
struct z_find_longest_prefix :
std::conditional_t<
can_apply< z_apply, Z, types >{},
z_constant<types>,
z_template<longest_prefix>
>::template result<Z, pop_back<types>>
{};
template<class Z, class types>
using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
struct empty_t {};
template<class Z>
struct z_find_longest_prefix<Z,types<>>:
std::conditional_t<
can_apply< Z::template result >,
types<>,
empty_t
>
{};
find_longest_prefix
如果没有有效的前缀将编译失败。
template<class Z, class types>
using z_apply_longest_prefix_t =
z_apply< Z, z_find_longest_prefix_t<Z, types> >;
template<class src, template<class...>class... Zs>
struct use_template;
template<class src, template<class...>class... Zs>
using use_template_t = typename use_template<src, Zs...>::type;
template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
struct use_template< Z0<Ts...>, Zs... > {
using type=Z0<
z_apply_longest_prefix_t<
z_template<Zs>, types<Ts...>
>...
>;
};
以及最多拼写错误和类似问题,已完成。
可以清理很多。可能不需要整个 z_
子系统。每当进行严肃的元编程时,我都倾向于 运行 它,因为突然之间,我编写的适用于类型的任何元函数现在都可以用在模板上了。
reverse
需要一些工作才能高效完成。
can_apply
在
pop_front
是微不足道的。
apply
应该很容易。
这是"find shortest prefix"。
实用工具:
// pack that is easy to append to
template<class... Ts>
struct pack{
template<class... T1s>
using append = pack<Ts..., T1s...>;
};
template<class...> using void_t = void;
肉:
// find the shortest proper prefix Ts... of [T, Rest...]
// such that A<Ts...> is well-formed, and return it.
template<template<class...> class A, // template we are going to look at
class AlwaysVoid, // for void_t
class Current, // pack containing the types we are testing next
class T, // next type to add to pack if test fails
class... Rest> // remaining types
struct find_viable
: find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
// picked if A<Ts...> is well-formed
template<template<class...> class A, class... Ts, class T, class...Rest>
struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
using type = A<Ts...>;
};
// Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
// the extra void at the end is slightly hackish - find_viable only checks
// proper prefixes, so we add an extra dummy type at the end.
template<template<class...> class A, class... Ts>
using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
然后使用它:
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts,
template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<find_viable_t<Templates, Ts...>...>;
};
这看起来很有趣,所以我尝试了一下;我并不是说这在任何方面都比现有答案更好,但也许有些人会发现它更清楚:
namespace detail {
template<typename...>
using void_t = void;
template<template<typename...> class, typename, typename, typename EnableT = void>
struct fill_template;
template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
struct fill_template<
TemplateT, TupleT, std::index_sequence<Is...>,
void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
>
{
using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
};
template<
template<typename...> class TemplateT, typename TupleT,
std::size_t I, std::size_t N
>
struct fill_template_dispatcher
{
template<typename T, typename = typename T::type>
static constexpr std::true_type test(int) { return {}; }
template<typename T>
static constexpr std::false_type test(long) { return {}; }
using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
using type = typename std::conditional_t<
test<candidate_t>(0),
candidate_t,
fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
>::type;
};
template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
{
static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
};
} // namespace detail
template<typename TupleT, template<typename...> class... TemplateTs>
struct template_pack
{
static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
using type = std::tuple<typename detail::fill_template_dispatcher<
TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
>::type...>;
};
template<typename TupleT, template<typename...> class... TemplateTs>
using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
请注意,对于可变 class 模板,这将传递尽可能少的类型(参见测试中的 D
);对于尽可能以 many 类型传递的实现,请参阅代码的变体 here(在实现中仅更改了两行)。