【问题标题】:template metaprogramming :why flat type is failure模板元编程:为什么扁平类型是失败的
【发布时间】:2014-01-06 08:33:26
【问题描述】:

我想将树类型扁平化为扁平类型。示例:

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&lt;int,std::tuple&lt;int,long&gt;,int&gt;...我使用 gcc 4.8.1

【问题讨论】:

  • 您不需要中间的部分专业化(C&lt;ARGS...&gt; 的那个)。删除后会发生什么?
  • @Angew 如果我删除它。仍然无法工作。
  • 不能使用偏特化Flat&lt;C&lt;Args0...,C&lt;Args1...&gt;,Args2...&gt;&gt;Args0 已经匹配了C 的所有模板参数,因此无法推断出Args1Args2。 clang++ 给出了这个警告: warning: class template partial specialization contains template parameters that can be deduced;这个部分特化永远不会被使用
  • Here's a non-minimal solution。不幸的是,它适用于 clang++,但无法使用 g++4.8.1 编译当用 std::tuple 替换模板模板参数 TT 时,g++ 也接受它,这使我得出结论 不接受它 是一个 g++ 错误。

标签: c++ templates c++11


【解决方案1】:

首先解释这是如何工作的:

这是一个前向声明,因此我们可以将flatten 与单一类型一起使用。

template<class T>
struct flatten;

然后我们专门flatten 接受任何模板类型。 C 是一个模板模板参数,所以我们可以得到周围模板的类型。 例如。如果您将flatten 与参数std::tuple&lt;int, double&gt; 一起使用,那么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;                                                              

append 的特化,它从Target 检索类型列表(见上文)。 请注意,这里的C 仍然是std::tuple

因此,我们在列表中再添加一项,并通过应用先前的参数列表 Args1 然后添加 T 来创建新类型

    template< typename ...Args1, typename T >
    struct append<C<Args1...>, T> {
        using type = C<Args1..., T>;
    };

innerinner2 的转发声明

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&lt;&gt; 指定了 TargetFArgs 是由 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 启发) Edit2:解释代码

【讨论】:

  • @DyP 已修复 ;) 感谢您的测试 :-)
  • 啊,现在我可以看到递归了:)
  • @DyP 感谢您的示例,我已将示例重写为更像您的示例,适用于 g++ 和 clang++ :-)
  • @Vinzenz 你能添加解释代码的 cmets 吗?甚至更好的解释?我很难理解可变参数的这种用法。谢谢。
  • @Koushik 我已经添加了详细说明这是如何工作的。如果您遗漏了什么,请添加另一条评论。
【解决方案2】:

这是我的尝试。我试图记录正在发生的事情以使其清楚:

我们从扁平化开始。它需要一个类型。我们将在下面专门介绍它:

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 实现 Flatten 到一个空包:

template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> >:Flatten_append< Pack<>, Pack<Ts...> > {};

我们的目标是尽可能清楚地说明发生了什么。

现在,您会注意到这种设计的一个缺点是它会将 any 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&lt; blah, blah, blah &gt; 的每个特化更改为Flatten_append&lt; Pack, blah, blah, blah &gt;

这意味着您传入要显式展平的template,而代码只会展平template

实际上,这可能不需要,因为Pack 类型是从传入的左侧类型推导出来的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2017-01-07
    • 2018-02-19
    相关资源
    最近更新 更多