Is there any way to store the template parameter packs and reuse it later?

Viewed 243

I have very long parameter pack. I wonder is there any way to store the parameter pack and reuse it later. For example, if there are 2 templates:

template<class ... Types> struct A {};
template<class ... Types> struct B {};

I have a specialized type:

typedef A<int, int, float> A1_t;

Is there any operation can let me create a specialized type B which use the same parameter pack as A1_t? (B<int, int, float>). Is there any method to retrieve the <int, int, float> from A1_t or store it?

I want obtain a specialized type B1_t instead of creating the object of B1_t.

A and B describes completely different concept, so I cannot make B nested inside A.

moreover, I would also like to feed the parameter packs to specialize function templates.

template<class ...Ts>
C<Ts...> func1()
{
}

so I can directly call func1<int, int, float>() It will be nice if I can do something like this:

template<typename T>
transform<B, T> func1()
{
}

next step would be something similar to this:

template<template<class...Ts> templ>
B<Ts...> func2(Ts ...args)
{
}

So I can do func2<A1_t>(1, 2, 3.0f) directly.

1 Answers

Something like this? Using a type transformation based on partial specialization:

#include<type_traits>

template<template<typename...> class, typename>
struct with_tmpl_args_of;

template<template<typename...> class OtherTmpl, template<typename...> class Tmpl, typename... Args>
struct with_tmpl_args_of<OtherTmpl, Tmpl<Args...>> {
    using type = OtherTmpl<Args...>;
};

template<template<typename...> class OtherTmpl, typename T>
using with_tmpl_args_of_t = typename with_tmpl_args_of<OtherTmpl, T>::type;

// example

template<class ... Types> struct A {};
template<class ... Types> struct B {};

using A1_t = A<int, int, float>;

using B1_t = with_tmpl_args_of_t<B, A1_t>;

// test

static_assert(std::is_same_v<B1_t, B<int, int, float>>);

This is limited to class templates that do not use non-type template arguments. There is currently unfortunately no way to define template template parameters which accept both type and non-type template parameters in the same template template parameter's parameter.

Also beware of default arguments. This will not use OtherTmpl's default arguments, if one of Tmpl's default arguments matches that position in the template list and will fail if Tmpl's template list (including defaulted arguments) is larger than OtherTmpls.


Regarding the additional examples in your edit:

The second example works directly with the type transform I defined above:

template<typename T>
with_tmpl_args_of_t<B, T> func1()
{
}

The third one can be done like this:

template<typename A, typename... Ts>
with_tmpl_args_of_t<B, A> func2(Ts... args)
{
}

It guarantees that the return type has the same template arguments as A1_t, but it does accept all types as arguments, even if they don't match the types in the template arguments of A1_t. This should not usually be a problem. If the types are not convertible to the correct ones you will get an error at the point where you try the conversion.

If you must take the exact same types as in the template arguments of A1_t for function parameters, you can do something like (untested):

template<typename T>
struct func_with_tmpl_args_of;

template<template<typename...> class Tmpl, typename... Args>
struct func_with_tmpl_args_of<Tmpl<Args...>> {
    template<typename F>
    struct inner {
        constexpr inner(F f) : f(std::move(f)) {}
        constexpr static decltype(auto) operator()(Args... args) const {
            return f(std::forward<Args>(args)...);
        }
    private:
        F f;
    };
};

// example

template<typename T>
constexpr inline auto func2 = func_with_tmpl_args_of<T>::inner{[](auto... args)
  -> with_tmpl_args_of_t<B, T> {
    // actual function body
}};
Related