模板元编程:为什么平面类型是失败的
template metaprogramming :why flat type is failure
我想将树类型展平为平面类型。例:
typedef std::tuple<int,std::tuple<int,long>,int> tup;
Flat<tup>::type=>std::tuple<int,int,long,int>
我使用:
template<typename T>
struct Flat
{
using type=T;
};
template <template <typename ...> class C,typename...ARGS>
struct Flat<C<ARGS...> >
{
using type=C<ARGS...>;
};
template <template <typename ...> class C,typename ...ARGS0,typename...ARGS1,typename ...ARGS2>
struct Flat<C<ARGS0...,C<ARGS1...>,ARGS2...> >
:Flat<C<ARGS0...,ARGS1...,ARGS2...> >
{
};
void test(){
typedef std::tuple<int,std::tuple<int,long>,int> tup;
static_assert(std::is_same<typename Flat<tup>::type,std::tuple<int,int,long,int> >::value,"");
}
但我仍然std::tuple<int,std::tuple<int,long>,int>
...我使用 gcc 4.8.1
首先解释一下它是如何工作的:
这是一个前向声明,以便我们可以将flatten
与单个类型一起使用。
template<class T>
struct flatten;
然后,我们专门flatten
接受任何模板类型。C
是一个模板模板参数,因此我们可以获取周围模板的类型。 例如,如果您将flatten
与参数一起使用std::tuple<int, double>
那么C
将是std::tuple
.
FArgs 用于检索传递的模板的参数列表。在我刚才提到的示例中,它将是int, double
template< template< typename ... > class C, typename ...FArgs>
struct flatten<C<FArgs...>>
flatten
实现的其余部分现在始终可以访问C
包装器类型并FArgs
参数列表。
append
的前向声明,用于将参数B
追加到参数Target
中的项列表
template< typename Target, typename B >
struct append;
从Target
检索类型列表的append
专业化(见上文)。 请注意,此处C
仍然是std::tuple
。
因此,我们将向列表中再添加一个项,并通过应用上一个参数列表Args1
然后添加T
来创建新类型
template< typename ...Args1, typename T >
struct append<C<Args1...>, T> {
using type = C<Args1..., T>;
};
inner
和inner2
的预先申报
inner
用于循环访问提供的参数列表,并对每个参数应用inner2
,如果类型是另一个带有参数的模板,则再次inner
应用于类型。此模板必须是匹配的模板C
template< typename Target, typename ...Args >
struct inner;
template< typename Target, typename T >
struct inner2;
此inner2
处理所有C
模板类型,并在inner
的帮助下递归遍历其参数列表
template< typename Target, typename ...Args>
struct inner2<Target, C<Args...>> {
using type = typename inner<Target, Args...>::type;
};
如果使用这种inner2
专用化,则该类型将追加到最终结果中。
template< typename Target, typename T >
struct inner2 {
using type = typename append<Target, T>::type;
};
通过继承和应用每个参数来递归模板参数inner2
template< typename Target, typename T, typename ...Args>
struct inner<Target, T, Args...>
: inner<typename inner2<Target, T>::type, Args...>
{};
递归的结束条件。
template< typename Target, typename T >
struct inner<Target, T>
{
using type = typename inner2<Target, T>::type;
};
在这里,从上面触发了整个事情,C<>
指定了Target
,FArgs
是flatten
专用化提取的参数。
using type = typename inner<C<>, FArgs...>::type;
};
这里是整个代码:
#include <tuple>
#include <string>
template<class T>
struct flatten;
template< template< typename ... > class C, typename ...FArgs>
struct flatten<C<FArgs...>>
{
template< typename Target, typename ...B >
struct append;
template< typename ...Args1, typename T >
struct append<C<Args1...>, T> {
using type = C<Args1..., T>;
};
template< typename Target, typename ...Args >
struct inner;
template< typename Target, typename T >
struct inner2;
template< typename Target, typename ...Args>
struct inner2<Target, C<Args...>> {
using type = typename inner<Target, Args...>::type;
};
template< typename Target, typename T >
struct inner2 {
using type = typename append<Target, T>::type;
};
template< typename Target, typename T, typename ...Args>
struct inner<Target, T, Args...>
: inner<typename inner2<Target, T>::type, Args...>
{};
template< typename Target, typename T >
struct inner<Target, T>
{
using type = typename inner2<Target, T>::type;
};
using type = typename inner<C<>, FArgs...>::type;
};
int main() {
typedef flatten<std::tuple<int, float, double>>::type first;
static_assert(std::is_same<first, std::tuple<int, float, double>>::value, "First not the same");
typedef flatten<std::tuple<int, std::tuple<float, double>>>::type second;
static_assert(std::is_same<second, std::tuple<int, float, double>>::value, "Second not the same");
typedef flatten<std::tuple<int, std::tuple<char const *>, std::tuple<std::tuple<float, int>, double>>>::type third;
static_assert(std::is_same<third, std::tuple<int, char const *, float, int, double>>::value, "Third not the same");
typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<char const *>>>>, std::tuple<std::tuple<float, int>, double>>>::type fourth;
static_assert(std::is_same<fourth, std::tuple<int, char const *, float, int, double>>::value, "Fourth not the same");
typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<std::string>>>>, std::tuple<std::tuple<float, int>, double>>>::type fifth;
static_assert(std::is_same<fifth, std::tuple<int, std::string, float, int, double>>::value, "Fifth not the same");
}
编辑:我重写了实现,使其更具可读性和更短(受@DyP启发) 编辑2:解释代码
这是我对此的刺痛。 我试图记录正在发生的事情以使其清楚:
我们从Flatten开始。 它需要一个类型。 我们将在下面专门介绍它:
template<typename T>
struct Flatten;
这是我们的主力军。 获取 Src,并展平其内容并将其附加到 Dest:
template<typename Dest, typename Src>
struct Flatten_append;
空的右侧包装意味着返回左侧:
template<template<typename...>class Pack, typename... LHS>
struct Flatten_append< Pack<LHS...>, Pack<> > {
typedef Pack<LHS...> type;
};
第一个参数是 Pack<...> 的右侧应在处理前展平:
template<template<typename...>class Pack, typename... LHS, typename... RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<Pack<RHS0...>, RHSrest... > >:
Flatten_append< Pack<LHS...>, Pack< RHS0..., RHSrest... > >
{};
否则,非空的右侧包应将其第一个元素移动到左侧:(这将比上述匹配弱,因为它不太专业)
template<template<typename...>class Pack, typename... LHS, typename RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<RHS0, RHSrest... > >:
Flatten_append< Pack<LHS..., RHS0>, Pack< RHSrest... > >
{};
在Flatten_append到空包方面实现扁平化:
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> >:Flatten_append< Pack<>, Pack<Ts...> > {};
目标是尽可能清楚地了解正在发生的事情。
现在,您会注意到此设计的缺点是它会展平任何仅包含类型的template
。我们可能想传入我们想要扁平化的包。
template<template<typename...>class Pack, typename T>
struct Flatten;
template<template<typename...>class Pack, typename Dest, typename Src>
struct Flatten_append;
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> > : Flatten_append< Pack, Pack<>, Pack<Ts...> > {};
然后将Flatten_append< blah, blah, blah >
的每个专业更改为Flatten_append< Pack, blah, blah, blah >
。
这意味着您传入要显式展平的template
,而代码仅平展该template
。
实际上,这可能不需要,因为Pack
类型是从传入的左侧类型推断出来的。
- 从类型std::函数传递变量失败,尽管调用方期望的类型完全相同
- 如果原型是本地的,则使用流 I/O C++类型约束将失败
- 结构化绑定初始值设定项表单 { 赋值表达式 } 对于 clang 上的数组类型失败
- 使用自定义访问者时具有自定义类型的提升变体失败(源自 boost::static_visitor)
- 使用用户定义的类型 UDT 实例化 std::atomic<>。如果 UDT 具有虚函数,则 l 墨水将失败。为什么?
- 从具有部分专用化的boost:hana::set中提取类型失败
- 解决由于在哈希函数中使用了不完整的类型而导致的编译失败
- 在 static_assert 中使用 std::is_base_of 由于类型不完整而失败
- 元数据操作失败LNK2022错误 (8013118D):重复类型中的布局信息不一致 (选择设备参数):(0x020002
- 添加类型名会导致程序编译失败
- 模板类型推断失败(std::空作为谓词)
- 为什么类型铸件仅在这句话中失败
- 对于实际指针类型,用于检测类似指针(可取消引用)类型的模板函数失败
- 使用比较函数对引用类型失败
- 从istream读取自定义类型失败时的预期行为
- 设置MP3编码器mft输出类型失败
- 编译不完整类型失败;循环依赖
- 访问具有lambda的成员函数内部的类模板参数类型失败
- 检查输入类型失败,C++
- 将 GetWindowLongPtr() 强制转换为模板化类型失败