Search code examples
c++templatesconstructortemplate-specialization

Adding a constructor for specific case of templated class


I'm implementing a templated class Vect<std::size_t N>, a vector of N double. The general constructor takes a std::array<double, N> const& array, but I would like for instance for N=2 to have a constructor taking 2 double, to allow lighter instantiation.

I thought of a solution that works well (see below), but it feels a bit dirty. I've gone through many questions asked here and their answers, but I haven't quite found a solution that fits my problem nicely.

I'd also like to have an alias Vect2D for Vect<2>, either with a typedef or anything else that does the job (for instance, my solution below).

Here is some of the code of my class Vect:

template<std::size_t N> class Vect{
protected:
    std::array<double,N> v;
public:
    Vect(const std::array<double,N>& _v = {}): v(_v) {}
    
    /*  bunch of methods & operators  */
};

and here is my solution for now:

class Vect2D : public Vect<2>{
public:
    Vect2D(double x = 0, double y = 0): Vect({x,y}) {}
    Vect2D(const Vect<2>& other): Vect(other){}
};

(I had to add the last line because while Vect2D is a Vect<2>, Vect<2> is not a Vect2D, so when I use operators between two Vect2D, the returned object has type Vect<2> so if needed I convert it back to Vect2D with this last line, for instance if the return type of a function is Vect2D, and what I return is the result of an operator between two Vect2D. It does the job, but this is partially why I feel this solution is kinda dirty.)

I'd be grateful if anyone has a nice solution, otherwise it's not a big deal since my solution does what I want anyway.


Solution

  • What about avoiding the full class specialization and SFINAE enabling the two-double contructor only when N is 2?

    Something as

    template <std::size_t M = N, std::enable_if_t<(M == 2), int> = 0>
    Vect (double x = 0, double y = 0): v{x,y} {}
    

    I'd also like to have an alias Vect2D for Vect<2>, either with a typedef or anything else that does the job

    What about using using?

    using Vect2D = Vect<2>;
    

    The following is a full compiling example with external constructor implementation.

    #include <array>
    #include <type_traits>
    
    template <std::size_t N>
    class Vect
     {
       protected:
          std::array<double, N> v;
    
       public:
          Vect (std::array<double, N> const & v0 = {});
    
          template <std::size_t M = N, std::enable_if_t<(M == 2), int> = 0>
          Vect (double x = 0, double y = 0);
     };
    
    template <std::size_t N>
    Vect<N>::Vect (std::array<double, N> const & v0) : v{v0}
     { }
    
    template <std::size_t N>
    template <std::size_t M, std::enable_if_t<(M == 2), int>>
    Vect<N>::Vect (double x, double y) : v{x,y}
     { }
    
    // explicit deduction guide
    Vect (double, double) -> Vect<2u>;
    
    int main()
     {
       Vect<2u>  v2{1.0, 2.0};    // compile
       Vect      v2bis{1.0, 2.0}; // compile as Vect<2> (thanks to the deduction guide)
       // Vect<3u>  v3{1.0, 2.0}; // compilation error (no matching constructor)
     }