How do I strip a tuple<> back into a variadic template list of types?

孤人 提交于 2019-12-18 03:19:29

问题


Is there a way to strip a std::tuple<T...> in order to get it back to T...?

Example

Suppose vct<T...> is a pre-existing variadic class template,

using U = std::tuple<int,char,std::string>;
using X = vct<int,char,std::string>;
using Y = vct< strip<U> >;            // should be same as X

Notes

I know about std::tuple_element, but I need all the elements, in a form that is usable as T...

For reference, I have found this question, which is similar, but my needs are somewhat simpler (so I hope there is a simpler solution): all I need is the list of types that are in the tuple - I don't care about the actual values of a tuple instance.


回答1:


No, this is not possible. Argument packs are the result of type deduction, and they can't be produced in other contexts.

You could do something similar to what you're asking for this way:

template<template<typename...> class T, typename>
struct instantiate_with_arg_pack { };

template<template<typename...> class T, typename... Ts>
struct instantiate_with_arg_pack<T, std::tuple<Ts...>>
{
    using type = T<Ts...>;
};

template<typename... Ts>
struct vct { };

int main()
{
    using U = std::tuple<int,char,std::string>;
    using X = vct<int,char,std::string>;
    using Y = instantiate_with_arg_pack<vct, U>::type;
}

Actually, you don't need to hold the argument pack in a tuple: any variadic class template is OK:

template<template<typename...> class T, typename>
struct instantiate_with_arg_pack { };

template<
    template<typename...> class T, 
    template<typename...> class U, // <===
    typename... Ts
    >
struct instantiate_with_arg_pack<T, U<Ts...>>
//                                   ^^^^^^^^
{
    using type = T<Ts...>;
};

template<typename... Ts>
struct vct { };

int main()
{
    using U = std::tuple<int,char,std::string>;
    using X = vct<int,char,std::string>;
    using Y = instantiate_with_arg_pack<vct, X>::type;
    //                                        ^

    // Won't fire
    static_assert(
        std::is_same<Y, vct<int,char,std::string>>::value, 
        "Error!");
}

And here is a live example.




回答2:


template<typename>
struct strip;

template<typename ...T>
struct strip<std::tuple<T...>>
{
   using type = vct<T...>;
};

then use this as:

using Y = strip<U>::type;

Now Y is same as X.




回答3:


You can not directly "return" a parameter pack, so what you need is something like this:

template< typename... Ts >
struct vct
{ ... };

template< typename T >
struct make_vct;

template< typename... Ts >
struct make_vct< std::tuple< Ts... > >
{
    typedef vct< Ts... > type;
};

and use

using Y = make_vct< U >::type;



回答4:


The goal here is to be able to copy a parameter pack from an instance of a template, to another template. I didn't restrict it to tuple, because... why restrict it to tuple?

template<template<typename...>class Target, typename Src>
struct copy_pack_types;

template<template<typename...>class Target, template<typename...>class Src, typename... Ts>
struct copy_pack_types< Target, Src<Ts...> > {
  typedef Target<Ts...> type;
};

template<template<typename... Ts>class Target, typename Src>
using CopyPackTypes = typename copy_pack_types<Target, Src>::type;

#include <string>
#include <tuple>
template<typename... Ts> struct vct;
template<typename... Ts> struct vct2;
using U = std::tuple<int,char,std::string>;
using X = vct<int,char,std::string>;
using Y = CopyPackTypes< vct, U >;            // should be same as X
using Z = CopyPackTypes< vct2, U >;            // should be different from X

#include <iostream>
#include <type_traits>
int main() {
  std::cout << std::is_same< X, Y >::value << "\n";
  std::cout << std::is_same< Z, Y >::value << "\n";
  std::cout << std::is_same< Z, vct2<int,char,std::string> >::value << "\n";
}

output is "1 0 1" as expected.

The CopyPackTypes takes a target template, and a source type that was constructed from a parameter pack as its only argument. It then copies the parameter pack to the target template.

One standard technique is to carry parameter packs around is to create an otherwise no use type like:

template<typename...>
struct types {};

which only exists as a placeholder for a list of types. You can then pass a few of these to another template, and each pack doesn't "step on" each other. When you need to apply it to a target template, you use something like the above "CopyPackTypes" to apply it.

Similar techniques are used for packs of indexes:

template<size_t...>
struct seq {};

otherwise useless types that are "black slates" to carry clumps of parameters around.



来源:https://stackoverflow.com/questions/15418841/how-do-i-strip-a-tuple-back-into-a-variadic-template-list-of-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!