是否可以在不扩展的情况下“存储”模板参数包?


73

当我偶然发现此问题时,我正在尝试使用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中,而不将其解包。可能吗?如果不是,是否有某些原因不允许这样做?

Answers:


57

比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()
{}

1
很好的解决方法,我没有考虑使用部分模板专业化!
卢·图拉

@GMan:快速提问...这很有帮助,但是部分专业的版本实际上应该是typedef typename convert_in_tuple<Args...>::type type;,还是没关系?
杰森

@杰森:是的。令我惊讶的是,这么长时间没有得到敏锐的注意,我感到惊讶。:)
GManNickG 2011年

3
我会有点担心:虽然说“对待类型列表,或包含列表的特定类型的实例一样,这很诱人”,但根据我的经验,当您去做。例如,想象一个长度为1的列表,其中包含avariadic_typedef以及它如何与上述代码交互。现在想象一下每个传递给a的类型的列表convert_in_tuple以及它如何与上述代码交互。如果您开始设置一些间接级别,则将容器和内容物视为可互换会导致问题。
Yakk-Adam Nevraumont

1
我不明白这如何解决OP的问题。convert_in_tuple结构包含一个元组别名的别名。它表示的类型是带有Args ...参数包的元组,而不是带有Args ...参数包本身的元组。
user2813810 2015年

10

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

类似于以下内容:

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的代码,你只需要改变typename Tclass T,改变convert_in_tuple的参数是一个模板,模板的模板参数:template < template< template < typename ... > class > class T > struct convert_in_tuple {...}(!)。
Luc Touraille

1
@Luc:编辑为模板模板模板参数。更换typenameclass感觉有点可疑,因为该草案说“之间不存在语义差别class,并template模板参数”,你可以试试这个新的代码?
Ben Voigt

我在标准中找不到它,但是我想我记得对于模板模板参数,您不需要使用classtypename(因为模板类型不可避免地是一个类,而不是任何类型)。
Luc Touraille

@Luc:感谢它的指导,让它可以在VM的gcc 4.5.2中进行编译。现在正在努力使VM中的复制粘贴功能无法正常工作...
Ben Voigt

确实,该标准在§14.1.2中指出class和之间没有区别typename,但是恰好在§14.1.1中,该语法仅允许class在模板模板参数声明中使用关键字。尽管这可能看起来不一致,我认为理由是,就像我以前说过,一个模板的模板参数不能是任何类型(例如它不能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源自以下事实:现在已使用先前的列表实例化了该类型。


2

这是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() {}

1
@GManNickG的答案中没有递归,而且我认为使用原始参数包而不是variadic_typedef功能是一项功能。因此,我想说的是,这个答案更多的是降级而不是完善……
卢克·图拉耶

我知道不使用variadic_typedef的选项旨在作为一种功能,但是一个人的功能就是另一个人的错误。首先,我在这个线程上的原因是想找到一种方法来准确地执行我的回答。此外,@ GManNickG的解决方案中只有一个递归“调用”-当convert_in_tuple的variadic_typdef部分专业化“委托”到非专业化版本时。没有它,事情会更简单。最后,我选择了优化一词,不是为了使我的解决方案更好,而是更具体。我更改了措辞以反映这一点。
2013年

您可以删除的依赖上variadic_typedefconvert_in_tuple-有它取template<typename Pack> struct convert_in_tuple {};,然后专注template<template<typename...>class Pack, typename...Args> struct convert_in_tuple<Pack<Args...>> { typedef std::tuple<Args> type; }-现在任何variardic包可以被映射到tuple
Yakk-Adam Nevraumont
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.