【问题标题】:Is it possible to "store" a template parameter pack without expanding it?是否可以“存储”模板参数包而不扩展它?
【发布时间】:2011-06-09 03:57:39
【问题描述】:

当我偶然发现这个问题时,我正在尝试使用 C++0x 可变参数模板:

template < typename ...Args >
struct identities
{
    typedef Args type; //compile error: "parameter packs not expanded with '...'
};

//The following code just shows an example of potential use, but has no relation
//with what I am actually trying to achieve.
template < typename T >
struct convert_in_tuple
{
    typedef std::tuple< typename T::type... > type;
};

typedef convert_in_tuple< identities< int, float > >::type int_float_tuple;

当我尝试 typedef 模板参数包时,GCC 4.5.0 给我一个错误。

基本上,我想将参数包“存储”在 typedef 中,而不是解包。可能吗?如果不是,有什么理由不允许这样做吗?

【问题讨论】:

    标签: c++ templates c++11 variadic-templates


    【解决方案1】:

    另一种比 Ben 的方法更通用的方法如下:

    #include <tuple>
    
    template <typename... Args>
    struct variadic_typedef
    {
        // this single type represents a collection of types,
        // as the template arguments it took to define it
    };
    
    template <typename... Args>
    struct convert_in_tuple
    {
        // base case, nothing special,
        // just use the arguments directly
        // however they need to be used
        typedef std::tuple<Args...> type;
    };
    
    template <typename... Args>
    struct convert_in_tuple<variadic_typedef<Args...>>
    {
        // expand the variadic_typedef back into
        // its arguments, via specialization
        // (doesn't rely on functionality to be provided
        // by the variadic_typedef struct itself, generic)
        typedef typename convert_in_tuple<Args...>::type type;
    };
    
    typedef variadic_typedef<int, float> myTypes;
    typedef convert_in_tuple<myTypes>::type int_float_tuple;
    
    int main()
    {}
    

    【讨论】:

    • 非常好的解决方法,我没想过使用部分模板专业化!
    • @GMan:快速提问……这很有帮助,但部分专业化的版本实际上应该是typedef typename convert_in_tuple&lt;Args...&gt;::type type;,还是没关系?
    • @Jason:没错。我很惊讶我的答案这么久没有引起注意。 :)
    • 我会有点担心:虽然在我的当你这样做时,体验事物往往会混乱地爆炸。例如,假设一个长度为 1 的列表包含 variadic_typedef 以及它如何与上述代码交互。现在想象一个类型列表,每个类型都传递给convert_in_tuple,以及它如何与上述代码交互。如果您开始设置一些间接级别,将容器和内容视为可互换会导致问题。
    • 我不明白这如何解决 OP 的问题。 convert_in_tuple 结构包含元组别名的别名。它表示的类型是带有 Args ... 参数包的元组,而不是 Args ... 参数包本身。
    【解决方案2】:

    我认为不允许这样做的原因是它会很混乱,您可以解决它。您需要使用依赖倒置并使将参数包存储到工厂模板中的结构能够将该参数包应用于另一个模板。

    类似的东西:

    template < typename ...Args >
    struct identities
    {
        template < template<typename ...> class T >
        struct apply
        {
            typedef T<Args...> type;
        };
    };
    
    template < template<template<typename ...> class> class T >
    struct convert_in_tuple
    {
        typedef typename T<std::tuple>::type type;
    };
    
    typedef convert_in_tuple< identities< int, float >::apply >::type int_float_tuple;
    

    【讨论】:

    • 我在 GCC 4.5 上尝试了您的代码,您只需在 class T 中更改 typename T 并将 convert_in_tuple 参数更改为模板模板参数:template &lt; template&lt; template &lt; typename ... &gt; class &gt; class T &gt; struct convert_in_tuple {...} (!)。
    • @Luc:编辑为模板模板模板参数。用class 替换typename 感觉有点可疑,因为草案说“classtemplate 中没有语义差异模板参数.",你能试试这个新代码吗?
    • 我在标准中找不到它,但我想我记得对于模板模板参数你需要使用class而不是typename(因为模板类型不可避免地是一个类和不是任何类型)。
    • @Luc:可以在虚拟机的 gcc 4.5.2 中编译,感谢您的指点。现在正在努力让复制+粘贴从 VM 中工作......
    • 确实,标准在 §14.1.2 中说 classtypename 之间没有区别,但就在上面(在 §14.1.1 中),语法只允许 @987654334模板模板参数声明中的@关键字。尽管这看起来不一致,但我认为理由是,就像我之前所说的,模板模板参数不能是任何类型(例如,它不能是 intbool),所以也许委员会决定使用typename 会产生误导。无论如何,让我们回到主题:)!
    【解决方案3】:

    我发现 Ben Voigt 的想法对我自己的努力非常有用。我对其进行了一些修改,使其不仅适用于元组。对于这里的读者来说,这可能是一个明显的修改,但可能值得展示:

    template <template <class ... Args> class T, class ... Args>
    struct TypeWithList
    {
      typedef T<Args...> type;
    };
    
    template <template <class ... Args> class T, class ... Args>
    struct TypeWithList<T, VariadicTypedef<Args...>>
    {
      typedef typename TypeWithList<T, Args...>::type type;
    };
    

    TypeWithList 的名称源于该类型现在使用以前的列表进行实例化。

    【讨论】:

      【解决方案4】:

      这是 GManNickG 巧妙的偏特化技巧的一种变体。没有委托,并且通过要求使用 variadic_typedef 结构,您可以获得更多的类型安全性。

      #include <tuple>
      
      template<typename... Args>
      struct variadic_typedef {};
      
      template<typename... Args>
      struct convert_in_tuple {
          //Leaving this empty will cause the compiler
          //to complain if you try to access a "type" member.
          //You may also be able to do something like:
          //static_assert(std::is_same<>::value, "blah")
          //if you know something about the types.
      };
      
      template<typename... Args>
      struct convert_in_tuple< variadic_typedef<Args...> > {
          //use Args normally
          typedef std::tuple<Args...> type;
      };
      
      typedef variadic_typedef<int, float> myTypes;
      typedef convert_in_tuple<myTypes>::type int_float_tuple; //compiles
      //typedef convert_in_tuple<int, float>::type int_float_tuple; //doesn't compile
      
      int main() {}
      

      【讨论】:

      • @GManNickG 的回答中没有递归,我认为使用原始参数包而不是 variadic_typedef 的能力是一个功能。因此,我会说这个答案与其说是一种改进,不如说是一种退化......
      • 我知道不使用 variadic_typedef 的选项是为了成为一项功能,但一个人的功能是另一个人的错误。我首先出现在这个线程上的原因是为了找到一种方法来完全按照我的答案在这里做。此外,@GManNickG 的解决方案中有一个递归“调用”——当 convert_in_tuple 的 variadic_typdef 部分特化“委托”到非特化版本时。没有它,事情会稍微简单一些。最后,我选择细化这个词不是为了让我的解决方案更好,而是更具体。我更改了措辞以反映这一点。
      • 您可以删除对variadic_typedef 的依赖,用于convert_in_tuple -- 使用template&lt;typename Pack&gt; struct convert_in_tuple {};,然后专门化template&lt;template&lt;typename...&gt;class Pack, typename...Args&gt; struct convert_in_tuple&lt;Pack&lt;Args...&gt;&gt; { typedef std::tuple&lt;Args&gt; type; } -- 现在任何变量包都可以映射到tuple
      猜你喜欢
      • 1970-01-01
      • 2014-10-30
      • 2015-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-12
      相关资源
      最近更新 更多