Is there a way to strip a std::tuple
in order to get it back to T...
?
Example
Suppose
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
.
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;
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.
Using c++17 std::apply
.
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
auto my_y = std::apply(
[](auto... ts) {
using Y = vct<decltype(ts)...>;
return Y{};
},
U{});
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.