Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++17

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.

like image 975
Wang Avatar asked Feb 27 '20 02:02

Wang


People also ask

Can we pass Nontype parameters to templates?

Template non-type arguments in C++It is also possible to use non-type arguments (basic/derived data types) i.e., in addition to the type argument T, it can also use other arguments such as strings, function names, constant expressions, and built-in data types.

Can a template be a template parameter?

Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.

Can template have default parameters?

You cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };

What is a purpose is template parameter?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


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
}};
like image 157
walnut Avatar answered Oct 18 '22 04:10

walnut