7

I would like to create a generic vector class and create specializations for a few cases. Something like this (it does not compile, but hopefully communicates my intentions):

template<int dim, typename T = float>
class Vector
{
public:
    typedef Vector<dim, T> VecType;

    Vector() { /**/ }
    Vector(const VecType& other) { /**/ )
    Vector& operator=(const VecType& other) { /**/ }

    VecType operator+(const VecType& other) { /**/ }    
    VecType operator-(const VecType& other) { /**/ }    
    T operator*(const VecType& other) { /**/ }

private:
    std::array<T, dim> elements;
};

template<int dim, typename T>
class Vector<2>
{
public:
    T x() const { return elements[0]; }
    T y() const { return elements[1]; }
};

template<int dim, typename T>
class Vector<3>
{
public:
    T x() const { return elements[0]; }
    T y() const { return elements[1]; }
    T z() const { return elements[2]; }
};

In other words, I want the default type of the elements to be float and I want to have x() and y() accessor methods for the dim = 2 case, and x(), y() and z() for the dim = 3 case. I'm a little confused by the error messages:

vector.h:56:10: error: declaration of ‘int dim’

vector.h:6:10: error: shadows template parm ‘int dim’

(same for T).

How can I do this correctly? (if it's possible)

Community
  • 1
  • 1
Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180

3 Answers3

12

1.

When partially specializing a template, only supply the template parameters that are actually a parameter. Since you've already fixed dim to be 2 or 3, there's no need to specify it again.

template<typename T>
class Vector<2, T>
{
   ....

2.

Specializing a class really means changing the whole declaration. Therefore, the members s of the generic Vector<dim, T> will not be available in the specialized Vector<2, T>. You could make the generic Vector<dim, T> as into an internal base class, and create a subclass just for specialization:

template<int dim, typename T>
class VectorImpl;

...

template<int dim, typename T = float>
class Vector : public VectorImpl<dim, T> {};

template<typename T>
class Vector<2, T> : public VectorImpl<2, T>
{
public:
   T x() const { ... }
};

3.

You don't need to define VecType! Inside a template, you could just use Vector. It will automatically be deduced to refer to the class with the right parameters.

The end result that compiles:

#include <array>

template<int dim, typename T>
class VectorImpl
{
public:
    //typedef Vector<dim, T> VecType;

    VectorImpl() {  }
    VectorImpl(const VectorImpl& other) {  }
    VectorImpl& operator=(const VectorImpl& other) { return *this; }

    VectorImpl operator+(const VectorImpl& other) { return *this; }
    VectorImpl operator-(const VectorImpl& other) { return *this; }
    T operator*(const VectorImpl& other) { return 0; }

protected:
    std::array<T, dim> elements;
};

template <int dim, typename T = float>
class Vector : public VectorImpl<dim, T> {};

template<typename T>
class Vector<2, T> : public VectorImpl<2, T>
{
public:
    T x() const { return this->elements[0]; }
    T y() const { return this->elements[1]; }
};

template<typename T>
class Vector<3, T> : public VectorImpl<2, T>
{
public:
    T x() const { return this->elements[0]; }
    T y() const { return this->elements[1]; }
    T z() const { return this->elements[2]; }
};

int main()
{
    Vector<2> v;
    Vector<3> vv;
    v + v;
    vv.z();
}
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Regarding #2, request for clarification. Your text reads: `You could make the generic Vector as into a private base class...` The example code uses `public` base class. Is this intentional or accidental? I suppose both cases are actually interesting and worthy -- `private` vs `protected` vs `public` inheritance for partial template specialisation. – kevinarpe May 17 '15 at 12:39
  • @kevinarpe: I meant an "internal" base class which is not supposed to be referred directly out of the library (Edited). The base class needs to be public otherwise all methods in it are not accessible outside of the class. – kennytm May 17 '15 at 14:04
  • I think it is possible to use private or protected inheritance, but `using` statements are required to 'elevate' the access of members to public. – kevinarpe May 18 '15 at 03:58
3

The partial specialization should be something like this:

template <int Dim, typename T = float> class Vector; // primary

template <typename T> class Vector<2, T> { /* ... */ };
template <typename T> class Vector<3, T> { /* ... */ };
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 4
    in 14.5.5/1 : "The template parameter list of a specialization shall not contain default template argument values." – fefe Dec 27 '11 at 13:59
0

You can do it like this:

#include <array>
template<int dim, typename T = float>
class Vector
{
public:
    typedef Vector<dim, T> VecType;
    Vector() { /**/ }
    Vector(const VecType& other) { /**/ }
private:
    std::array<T, dim> elements;
};

template<typename T>
class Vector<2, T>
{
public:
    T x() const { return elements[0]; }
    T y() const { return elements[1]; }
private:
    std::array<T, 2> elements;
};

template<typename T>
class Vector<3, T>
{
public:
    T x() const { return elements[0]; }
    T y() const { return elements[1]; }
    T z() const { return elements[2]; }
private:
    std::array<T, 3> elements;
};

int main(int argc, char **argv)
{
    Vector<2> v2;
    v2.x();
    Vector<3> v3;
    v3.z();
    return 0;
}

This can compile in gcc 4.5.2 (Don't run it ...).

However, in this way, you will not be able to use any of the member variable of member function define in your main template in the specialization.

fefe
  • 3,342
  • 2
  • 23
  • 45