Template可变模板的模板参数扩展
template template parameter expansion for variadic templates
我最近了解到模板模板参数的存在,现在想知道这样的事情是否可能:
template<template<class... > class Container, typename... args>
struct ContainerTemplate
{
using container = std::tuple<Container<args...>...>;
};
我想要的是一个模板,它得到一个容器或其他模板类作为模板模板参数,然后扩展模板参数的其余部分,如果容器有N个模板参数,我给N * M个模板参数,我得到M个模板实例化与N个模板参数,例如:
ContainerTemplate<std::vector, int, short, char>
//assuming std::vector takes only 1 arg for simplicity
应该产生
container = std::tuple<std::vector<int>, std::vector<short>, std::vector<char>>
,
ContainerTemplate<std::map, int, int, short, short>
//assuming std::map takes only 2 args for simplicity
应该产生
container = std::tuple<std::map<int, int>, std::map<short, short>>
有什么办法可以做到吗?问题是你是否能找出Container有多少个模板参数。
编辑:如果需要在大小为N
的元组中传递额外的参数,则可以。ContainerTemplate<std::map, std::tuple<int, int>, std::tuple<short, short>>
Edit2:所以我找到了一种方法来确定模板模板参数的数量
template<typename... T>
struct TypeList
{
static const size_t Size = sizeof...(T);
template<typename T2>
struct PushFront
{
typedef TypeList<T2, T...> type_list;
};
};
template<template<class...> class Template, typename... Args>
struct SizeofTemplateTemplate
{
static const size_t Size = 0;
typedef TypeList<> type;
};
template<template<class...> class Template, typename Arg, typename... Args>
struct SizeofTemplateTemplate<Template, Arg, Args...>
{
template<typename... Args>
struct Test;
typedef char yes[1];
typedef char no[2];
template<typename... Args>
struct Test<TypeList<Args...>>
{
template<template<class...> class Template>
static yes& TestTemplate(Template<Args...>* arg);
template<template<class...> class Template>
static no& TestTemplate(...);
};
typedef typename SizeofTemplateTemplate<Template, Args...>::type::PushFront<Arg>::type_list type;
static const size_t Size = sizeof(Test<type>::TestTemplate<Template>(0)) == sizeof(yes) ? type::Size : SizeofTemplateTemplate<Template, Args...>::Size;
};
有了这个,下面的代码将打印2
std::cout << SizeofTemplateTemplate<std::vector, int, std::allocator<int>, int, int>::Size << std::endl;
我现在唯一的问题是,dyp的解决方案崩溃的visual studio编译器xD
Edit3:这里的原始问题的完整解决方案:https://stackoverflow.com/a/22302867/1366591
根据您的第一次尝试是不可能的,但根据您的编辑是可能的,其中参数被打包在std::tuple
中。在这种情况下,下面的模板Embed
在每个tuple
中接受参数并将它们嵌入Container
。
参见实例
template<template<class... > class Container, typename P>
struct Embed_t;
template<template<class... > class Container, typename... T>
struct Embed_t <Container, std::tuple <T...> >
{
using type = Container <T...>;
};
template<template<class... > class Container, typename P>
using Embed = typename Embed_t <Container, P>::type;
template<template<class... > class Container, typename... P>
struct ContainerTemplate
{
using container = std::tuple<Embed <Container, P>...>;
};
一般来说,将...
放在...
中是非常棘手的,并且只能在有限的情况下发生(我只以有用的方式管理过一次)。
这里有一个不需要将模板模板参数预先打包为元组的解决方案。这种打包是自动完成的,您只需要提供在一个元组中打包多少个参数(N
)。
#include <tuple>
template<template<class...> class Container, int N>
struct join_n_impl
{
template<class ArgTuple, int I = 0, class Joined = std::tuple<>>
struct helper;
template<class Arg, class... Rest, int I, class... Joined>
struct helper<std::tuple<Arg, Rest...>, I, std::tuple<Joined...>>
: helper<std::tuple<Rest...>, I+1, std::tuple<Joined..., Arg>>
{};
template<class Arg, class... Rest, class... Joined>
struct helper<std::tuple<Arg, Rest...>, N, std::tuple<Joined...>>
{
using type = Container<Joined...>;
using rest = std::tuple<Arg, Rest...>;
};
template<class... Joined>
struct helper<std::tuple<>, N, std::tuple<Joined...>>
{
using type = Container<Joined...>;
using rest = std::tuple<>;
};
};
template<template<class...> class Container, int N, class ArgTuple>
using join_n = typename join_n_impl<Container, N>::template helper<ArgTuple>;
template<template<class...> class Container, int N, class Args,
class Collected = std::tuple<>>
struct pack_n;
template<template<class...> class Container, int N, class... Args,
class... Collected>
struct pack_n<Container, N, std::tuple<Args...>, std::tuple<Collected...>>
{
static_assert(sizeof...(Args) % N == 0,
"Number of arguments is not divisible by N.");
using joiner = join_n<Container, N, std::tuple<Args...>>;
using joined = typename joiner::type;
using rest = typename joiner::rest;
using type = typename pack_n<Container, N, rest,
std::tuple<Collected..., joined>>::type;
};
template<template<class...> class Container, int N, class... Collected>
struct pack_n<Container, N, std::tuple<>, std::tuple<Collected...>>
{
using type = std::tuple<Collected...>;
};
使用例子:
template<class, class>
struct test {};
#include <iostream>
template<class T>
void print_type(T) { std::cout << __PRETTY_FUNCTION__ << "n"; }
int main()
{
using to_pack = std::tuple<int, double, int, char, int, bool>;
print_type( pack_n<test, 2, to_pack>::type{} );
}
所以我实际上设法找到一种方法来解决我的问题。我将把iavr的答案作为解决方案,因为语法很好,它也允许使用模板重载。因此,为了完整起见,为了证明它确实是可能的:
template<typename... T>
struct TypeList
{
static const size_t Size = sizeof...(T);
template<typename T2>
struct PushFront
{
typedef TypeList<T2, T...> type_list;
};
};
template<template<class...> class Template, typename... Args>
struct SizeofTemplateTemplate
{
static const size_t Size = 0;
typedef TypeList<> type;
};
template<template<class...> class Template, typename Arg, typename... Args>
struct SizeofTemplateTemplate<Template, Arg, Args...>
{
typedef char yes[1];
typedef char no[2];
template<typename...>
struct Test;
template<typename... args>
struct Test<TypeList<args...>>
{
template<template<class...> class Testee>
static yes& TestTemplate(Testee<args...>* arg);
template<template<class...> class Testee>
static no& TestTemplate(...);
};
typedef typename SizeofTemplateTemplate<Template, Args...>::type::PushFront<Arg>::type_list type;
static const size_t Size = sizeof(Test<type>::TestTemplate<Template>(0)) == sizeof(yes) ? type::Size : SizeofTemplateTemplate<Template, Args...>::Size;
};
template<template<class...> class Template, size_t N, typename... Args>
struct GenerateNTuple;
template<template<class...> class Template, typename... Args>
struct GenerateNTuple<Template, 0, Args...>
{
using type = TypeList<>;
using rest = TypeList<Args...>;
};
template<template<class...> class Template, size_t N, typename Head, typename... Args>
struct GenerateNTuple<Template, N, Head, Args...>
{
using type = typename GenerateNTuple<Template, N - 1, Args...>::type::template PushFront<Head>::type_list;
using rest = typename GenerateNTuple<Template, N - 1, Args...>::rest;
};
template<template<class...> class Container, typename... args>
struct DeduceType;
template<template<class...> class Container, typename... args>
struct DeduceType<Container, TypeList<args...>>
{
using type = Container<args...>;
};
template<template<class...> class Template, typename... Args>
struct ContainerTemplate;
template<template<class...> class Template, typename... Args>
struct ContainerTemplate<Template, TypeList<Args...>>
{
using packed = GenerateNTuple<Template, SizeofTemplateTemplate<Template, Args...>::Size, Args...>;
using type = typename ContainerTemplate<Template, typename packed::rest>::type::template PushFront<typename DeduceType<Template, typename packed::type>::type>::type_list;
};
template<template<class...> class Template>
struct ContainerTemplate<Template, TypeList<>>
{
using type = TypeList<>;
};
template<template<class...> class Template, typename... Args>
using ContainerTypeList = typename ContainerTemplate<Template, TypeList<Args...>>::type;
的用法如下:
template<typename T>
using vec = std::vector<T>;
std::cout << typeid(ContainerTypeList<vec, int, short>).name() << std::endl;
我已经提出了另一个解决方案,根据您的第一个要求进行全自动包装。需要注意的是,实现并不是完全可变的:你必须为1、2、3个参数的模板专门化。然而,使用完全符合您最初的要求。
这可能与我没有仔细研究的dyp的解决方案相似。
同样,参见实际示例。
简而言之,将模板模板打包成这样的普通模板:
template<template<class> class>
struct Temp1;
template<template<class, class> class>
struct Temp2;
那么,ContainerTemplate
的主要定义(例如对于2个参数)是
template<
template<class, class> class Container,
typename T1, typename T2, typename... T
>
struct ContainerTemplate <Temp2<Container>, T1, T2, T...>
{
using container = Join <
std::tuple<Container<T1, T2> >,
typename ContainerTemplate<Temp2<Container>, T...>::container
>;
};
template<template<class, class> class Container>
struct ContainerTemplate<Temp2<Container> >
{
using container = std::tuple<>;
};
其中Join
是连接(参见实际示例的定义)。
最后,给定例如
template<class> class Vector { };
template<class, class> class Map { };
的用法非常好:
ContainerTemplate<Temp1<Vector>, int, short, char>
ContainerTemplate<Temp2<Map>, int, int, short, short>
开始使用Boost Mpl
我选择通过首先将输入"配对"为mpl::pair
的向量来解决映射情况。
#include <boost/mpl/transform.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/vector.hpp>
#include <vector>
#include <map>
namespace mpl = boost::mpl;
namespace detail
{
using namespace mpl;
template <template <typename...> class Container, typename... T>
using unary = typename transform<vector<T...>, Container<_1> >::type;
namespace binary_impl
{
template <typename MplVector> struct pairs;
template <> struct pairs<mpl::vector<> >
{
using type = mpl::vector<>;
};
template <typename A, typename B, typename... T>
struct pairs<mpl::vector<A, B, T...> >
{
using type = typename mpl::push_front<
typename pairs<mpl::vector<T...> >::type,
mpl::pair<A, B>
>::type;
};
}
template <template <typename...> class Container, typename... T>
using binary = typename transform<
typename binary_impl::pairs<vector<T...> >::type,
Container<apply_wrap1<first<>, _1>, apply_wrap1<second<>, _1> >
>
::type;
}
template <typename K, typename V, typename stuff = std::less<K> >
struct MyMap : std::map<K,V,stuff> { using std::map<K, V>::map; };
template <typename... T> using make_vectors = detail::unary<std::vector, T...>;
template <typename... T> using make_pairs = detail::binary<std::pair, T...>;
template <typename... T> using make_mymaps = detail::binary<MyMap, T...>;
#include <iostream>
#include <string>
int main()
{
auto vectors = make_vectors<int, char, double> { };
auto pairs = make_pairs <int, char, int, std::string, int, double> { };
auto mymaps = make_mymaps <int, char, int, std::string, int, double> { };
}
由于某种原因,它不能与实际的std::map
一起工作,但它可以与我的std::pair
或我自己的(std::map<>
派生)MyMap
类型一起工作。(如果有人能解释一下原因,我很乐意知道)。
参见Live On Coliru
这是使用std::tuple的另一个变体。我使用了@ACB中的一些代码来计算模板参数计数。
#include <tuple>
template<template<typename...> class Template, typename... Args>
struct TemplateArgCount
{
static const int value = 0;
};
template<template<typename...> class Template, typename Arg, typename... Args>
struct TemplateArgCount<Template, Arg, Args...>
{
typedef char small[1];
typedef char big[2];
template<typename... A>
struct Test
{
template<template<typename...> class T>
static small& test(T<A...>*);
template<template<typename...> class T>
static big& test(...);
};
static const int value = sizeof(Test<Arg, Args...>::template test<Template>(0)) == sizeof(small)
? sizeof...(Args)+1
: TemplateArgCount<Template, Args...>::value;
};
template<typename GlobalResult, typename LocalResult, template<typename...> class Template, int Count, int Pos, typename... Args>
struct TemplateTuplesImpl;
template<typename... GlobalResult, typename... LocalResult, template<typename...> class Template, int Count, typename Arg, typename... Args>
struct TemplateTuplesImpl<std::tuple<GlobalResult...>, std::tuple<LocalResult...>, Template, Count, Count, Arg, Args...>
: TemplateTuplesImpl<std::tuple<GlobalResult..., Template<LocalResult...>>, std::tuple<>, Template, Count, 0, Arg, Args...>
{
};
template<typename GlobalResult, typename... LocalResult, template<typename...> class Template, int Count, int Pos, typename Arg, typename... Args>
struct TemplateTuplesImpl<GlobalResult, std::tuple<LocalResult...>, Template, Count, Pos, Arg, Args...>
: TemplateTuplesImpl<GlobalResult, std::tuple<LocalResult..., Arg>, Template, Count, Pos+1, Args...>
{
};
template<typename... GlobalResult, typename ...LocalResult, template<typename...> class Template, int Count>
struct TemplateTuplesImpl<std::tuple<GlobalResult...>, std::tuple<LocalResult...>, Template, Count, Count>
{
using type = std::tuple<GlobalResult..., Template<LocalResult...>>;
};
template<template<class... Params> class Container, typename... Args>
struct TemplateTuples
{
static const int ParamSize = TemplateArgCount<Container, Args...>::value;
static const int ArgSize = sizeof...(Args);
static_assert(ParamSize > 0, "Arguments list does not match template class param list!");
static_assert(ArgSize%ParamSize == 0, "Argument list not in multiples of template class param count!");
using type = typename TemplateTuplesImpl<std::tuple<>, std::tuple<>, Container, ParamSize, 0, Args...>::type;
};
用法如下:
#include <type_traits>
#include <utility>
int main()
{
static_assert(std::is_same<TemplateTuples<std::pair, int, short, float, double>::type,
std::tuple<std::pair<int, short>, std::pair<float, double>>
>::value, "Does not match :-(");
return 0;
}
在摆弄了这个线程的各种解决方案之后,我决定了这个解决方案:
二维"元组包",即tuple
- 我不想使用容器,但是没有"默认可变参数"这样的东西。所以元组系统允许2D元组包的默认参数,而不是可变类型包。
- 如果需要,每个子元组可以有不同数量的参数。这允许任何默认模板参数发挥作用-即在子元组小于引用的template-template (t_tempMPlex)指定的情况下。
- 我也想把结果放在任何持有人,可能能够持有的结果-即元组,变量,或任何其他可变持有人,用户可能想要填充类型(t_tempVarHolder)。
// Multiplex templates with 2-dimensional tuple-types and contain them in some type of variant/tuple/etc container.
template < template<class... > class t_tempMPlex, class t_TyTpTPack >
struct _MPlexTPack2DHelp_2;
template < template<class... > class t_tempMPlex, class ... t_TysExtractTPack >
struct _MPlexTPack2DHelp_2< t_tempMPlex, tuple< t_TysExtractTPack ... > >
{
typedef t_tempMPlex< t_TysExtractTPack ... > type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp;
template< template<class... > class t_tempMPlex, class ... t_TyTpsExtractTPack,
template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp<t_tempMPlex, tuple< t_TyTpsExtractTPack ... >, t_tempVarHolder >
{
using type = t_tempVarHolder< typename _MPlexTPack2DHelp_2< t_tempMPlex, t_TyTpsExtractTPack >::type ... >;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder = tuple >
struct MPlexTPack2D
{
using type = typename _MPlexTPack2DHelp< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder = tuple >
using MPlexTPack2D_t = typename MPlexTPack2D< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;
用法:这是我的使用场景:我正在编写一个XML解析器,它可以在任何字符类型中本地工作。我还想支持在重要的情况下切换文件的端序性质-即UTF32BE和UTF16BE -当然当我在一个小端序机器上时。
我有这些传输类型:
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_file;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_fixedmem;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_mapped;
我想为我的变体解析器提供一个默认参数,它实现了所有字符类型和端转换的可能性,但我也希望用户能够指定他们想要的小于这个值。
下面是我的xml_parser_var的声明:template < template < class ... > class t_tempTyTransport,
class t_TyTp2DCharPack >
class xml_parser_var;
其中t_tempTyTransport是上述_l_transport_*模板之一。
我将为t_TyTp2DCharPack提供一个默认参数:
tuple< tuple< char32_t, true_type >,
tuple< char32_t, false_type >,
tuple< char16_t, true_type >,
tuple< char16_t, false_type >,
tuple< char8_t, false_type > >
但是我希望用户能够指定更少-例如,也许用户程序员不关心UTF32文件,只关心UTF16和UTF8。如果删除UTF32字符类型,将在变体中节省大量的二进制空间。
总之,长话短说,这就是我的想法。我喜欢它。它允许默认参数发挥作用,也就是说,这与上面给出默认参数的相同:tuple< tuple< char32_t, true_type >,
tuple< char32_t >,
tuple< char16_t, true_type >,
tuple< char16_t >,
tuple< char8_t > >
以下是我的使用顺序:
typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack > _TyTpTransports;
_tytptransportss最终为:
tuple< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
t_tempTyTransport< char8_t > >
然后那个"tuple pack"可用于产生进一步的类型等。另外,如果我想要一个变量而不是一个元组,例如:
variant< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
t_tempTyTransport< char8_t > >
那么我用这个代替:
typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack, variant > _TyTpTransports;
Synposis:
typedef MPlexTPack2D_t< variant,
tuple< tuple< char32_t, true_type >,
tuple< char32_t >,
tuple< char16_t, true_type >,
tuple< char16_t >,
tuple< char8_t, false_type > > > _TyTuple2D;
static_assert( is_same_v< _TyTuple2D,
tuple< variant< char32_t, true_type >,
variant< char32_t >,
variant< char16_t, true_type >,
variant< char16_t >,
variant< char8_t, false_type > > > );
让我知道你们的想法
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 扩展可变参数模板中的变量名称
- 扩展C++生成的代码的模板参数类型名称
- 将元组类型扩展为可变参数模板?
- 嵌套参数包扩展失败
- 模板参数部分中有关包扩展的一些混淆
- 可变参数模板参数扩展 类型为 std::function 的类成员
- 如何使用 std::forward 精确地评估参数包的扩展?
- 参数包没有扩展'...'即使...被使用
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- 如何"unzipping"扩展参数包模式?
- 可变参数函数参数包扩展
- c++非类型参数包扩展
- 如何使用自己的参数扩展 PointNormal 类?
- C 功能参数扩展了多个类
- 模板模板参数 - 扩展为 std::array
- C/C++ 如何将宏参数扩展为引号之间的文本
- 没有类型定义的可变参数扩展器
- 可变参数扩展是否可以用作逗号运算符调用链
- Template可变模板的模板参数扩展