Search code examples
c++templatesc++11variadic-templatespartial-specialization

C++: Set one partial specialization equal to another


I have written a class template with the following signature:

template<typename ... Args> struct MyClass {};
template<typename ... A, B> struct MyClass< std::tuple<A ...>, B> {};

I have two closely related questions for this scenario, which both deal with the way partial specializations can be mapped onto each other. If, despite my more or less thorough search, these question have already been addressed here, I would also be glad with links to those threads.


Question 1: In the code, instead of writing

MyClass< std::tuple<>, B> myClass;

, I want to create the corresponding object as

MyClass<B> myClass;

My first approach to this was to use inheritance,

template<typename B>
struct Myclass<B> : public MyClass<std:tuple<>, B>
{
    typedef MyClass<std:tuple<>, B> Base;

    using Base::Function1;
    using Base::Function2;
    // ...
}

but, if I see it right, then I would have to bring the base-class functions to the derived-class namespace. Further, thinking more in principles, these classes shall be the "same" and not related via inheritance-like "is-an" relationship.

Is there any other way to do it, for example with using template aliasing (the using keyword)? E.g. as (but this does't work)

template<typename B> struct MyClass<B> = MyClass<std::tuple<>, B>;

Any other working alternatives are welcome, it shall set one partial specialization equal to the other without much overhead.


Question 2 is closely related at least as far I can see it. Generally, I would prefer to avoid writing "tuple" at all. So in the same non-working way as above, but more generally, I would want to write something like

template<typename ... A, typename B> struct MyClass<B, A ... > = MyClass<std::tuple<A ...>, B>;

(I interchanged the order of A ... and B in the specialization because at least Visual Studio wants to have the pack-expansion as the last parameter.)

Any help is appreciated, thanks in advance.


EDIT: It doesn't seem to work with template aliases, see here. Other alternatives are welcome.


Solution

  • You may use an extra level of indirection, something like:

    template<typename ... Args> struct MyClass_impl {};
    template<typename ... Ts, typename T> struct MyClass_impl<std::tuple<Ts ...>, T> {};
    
    namespace detail
    {
        template<typename T, typename ... Ts>
        struct Helper
        {
            using type = MyClass_impl<std::tuple<Ts...>, T>;
        };
    
        template<typename ... Args, typename ... Ts>
        struct Helper<std::tuple<Args...>, Ts...>
        {
            using type = MyClass_impl<std::tuple<Args...>, Ts...>;
        };
    
    }
    
    template <typename ... Ts>
    using MyClass = typename detail::Helper<Ts...>::type;