Search code examples
c++c++17variadic-templatestemplate-meta-programmingboost-hana

Base case specialization for recursive variadic template


My target is to define a Recursive class, templated on an int N and one or more types T, ...Ts, which should behave like a std::pair with

  • a std::array of N items of type T as the first,
  • and, as second, an optional std::vector of Recursive instances templated on the same N and on the remaining template arguments Ts....

In trying to write down the class given the above requirements, I've come up with this non-working code (where I've also defined some necessary, as they help a lot, aliases for two instantiations of Recursive), and I don't know if I have mis-designed what I described above (or if it is an ill-formed description!), or if I'm misusing the language syntax.

#include <array>
#include <boost/hana/fwd/optional.hpp>
#include <boost/hana/optional.hpp>
#include <string>
#include <utility>
#include <vector>

template <int N, typename T1, typename T2, typename ...Ts>
struct Recursive
    : std::pair<std::array<T1, N>, 
                boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>> {};

template <int N, typename T>
struct Recursive<N, T> : std::array<T, N> {};

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    using boost::hana::nothing;
    Recursive2<int> x(std::make_pair(std::array<int, 2>{0,0}, nothing));
}

I'll add some troubleshooting that I've done so far. In the following the template specilization seems to work just fine.

#include <iostream>

template <int N, typename T, typename ...Ts>
struct Recursive {
    void operator()(){ std::cout << "general\n"; }
};

template <int N, typename T>
struct Recursive<N, T> {
    void operator()(){ std::cout << "specialized\n"; }
};

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    Recursive2<int>{}();
    Recursive2<int>{}();
    Recursive2<int,int>{}();
}

Solution

  • You have several issues:

    • Your specialization doesn't match your primary template

      template <int N, typename T1, typename T2, typename ...Ts> struct Recursive; requires at least 3 parameters. I think it should be a specialization and primary template should be:

      template <int N, typename T1, typename ...Ts>
      struct Recursive;
      
    • template <int N, typename T> struct Recursive<N, T> doesn't behave like a std::pair (as you state your requirement, else your usage is wrong), you probably want something like:

      template <int N, typename T>
      struct Recursive<N, T> : std::pair<std::array<T, N>, decltype(boost::hana::nothing)>
      
    • You need to "forward" the constructors of the base class, (Composition instead of inheritance might be an option too, or traits to define type to use) or change way to construct the object.

    Result is:

    template <int N, typename T1, typename ...Ts>
    struct Recursive;
    
    template <int N, typename T1, typename T2, typename ...Ts>
    struct Recursive<N, T1, T2, Ts...>
        : std::pair<std::array<T1, N>,
                    boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>
                                >
    {
        using std::pair<
            std::array<T1, N>,
            boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>>::pair;
    };
    
    template <int N, typename T>
    struct Recursive<N, T>
        : std::pair<std::array<T, N>, decltype(boost::hana::nothing)>
    {
        using std::pair<std::array<T, N>, decltype(boost::hana::nothing)>::pair;
    };
    
    template<typename ...T>
    using Recursive2 = Recursive<2u, T...>;
    
    template<typename ...T>
    using Recursive3 = Recursive<3u, T...>;
    
    int main() {
        using boost::hana::nothing;
        Recursive2<int> x(std::make_pair(std::array<int,2>{0,0}, nothing));
    }
    
    

    Demo