3

I am trying to specialise some geometrical functions depending on 2D or 3D, specified by a template parameter. Best if I include some (very broken) code for a toy version of the problem:

template <typename T, int d>
class Point
{
public:
    int x;
    int y;
    int z;

    T add ()
    {
        return T(0);
    }

    template <>
    T add <T, 2> ()
    {
        return x + y;
    }

    template <>
    T add <T, 3> ()
    {
        return x + y + z;
    }
};

This code fails miserably to compile. I've tried lots of different combinations of template parameter formats and class definitions, and cannot find a way to do function specialisation on 'd' whilst leaving the 'T' general.

In my actual solution I'm trying to calculate things like gradients, curvature, interpolation, etc, specialised for 2D or 3D cases. Some things, like gradient calculations, can simply use the 'd' parameter to limit for-loop iterations. Others, like interpolation, require a separate function for 2D and 3D.

Any hints much appreciated!

Dave
  • 1,304
  • 1
  • 15
  • 19
  • 1
    What is the template parameter `T` for? Is it supposed to be the type for your coordinates? If so, why are x,y,z declared as `int` intead of as type `T`? – Vaughn Cato Dec 31 '12 at 05:14
  • just to clear the air about this: there is no such thing as "template specialization" what you want is "template overloading". – user1849534 Dec 31 '12 at 06:33
  • @Vaughn: this simple toy problem came from my need to have a 2D or 3D grid that can contain an arbitrary type at each grid point (although usually just int, float or double). Hence x,y(,z) as the coordinate and T as the type of value at that coordinate. – Dave Dec 31 '12 at 18:10

2 Answers2

7

I would suggest this solution:

template <typename T, int d> 
class Point : public Point<T, d-1>
{
   typedef Point<T, d-1> base;
   T m_value;
public:
    T add()
    {
        return m_value + base::add();
    }   

    //another method which returns you the point value
    template<int N>
    T get()
    {
       return N==d ? m_value : base::get<N>();
    }
};

template <typename T>
class Point<T,0>
{
protected:
    T add()
    {
        return T(); //default value which is zero for all builtin types
    }
    template<int N>
    T get() { return T(); }

};

Using this solution you can have as many points as you want, but greater than zero.

Point<int,1> p1;  //contains 1 point
Point<int,2> p2;  //contains 2 points
Point<int,3> p3;  //contains 3 points
Point<int,4> p4;  //contains 4 points
Point<int,5> p5;  //contains 5 points

auto x1 = p5.get<1>(); //get first point
auto x3 = p5.get<3>(); //get third point
auto x4 = p5.get<4>(); //get fourth point

Or have these typedefs for convenience:

typedef Point<int,2> Point2D;
typedef Point<int,3> Point3D;

//then use them
Point2D p2d;
Point3D p3d;

Well that is just a basic idea which can be further enhanced, supporting many useful functionalities. I just wrote get<> to demonstrate one functionality which seems to be useful.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    Much better approach. Only downside is the loose of meaningful names for members. – K-ballo Dec 31 '12 at 05:23
  • @K-ballo: But that is implementation detail then! – Nawaz Dec 31 '12 at 05:24
  • 1
    @K-ballo: You could always use enable_if<> to conditionally add `getX()`, `getY()`, ... to `d==1`, `d==2`, ... – Ben Jackson Dec 31 '12 at 05:43
  • @BenJackson: I think `get<1>` is more generic than `getX()`. Also, you can also iterate over all points for any `Point` at compile-time using `get()` syntax. So it enables compile-time iteration (which is used in the implementation of `get<>` itself). – Nawaz Dec 31 '12 at 05:44
  • 1
    @Ben Jackson: You can't condition `getX()` with `enable_if` because there is no template parameter there. And if we were going that way then I like `get<0>` better. – K-ballo Dec 31 '12 at 05:45
  • @Nawaz: I think `get<1>` better be the generic way to say `getY()`, not `getX()` ;) – K-ballo Dec 31 '12 at 05:45
  • 1
    Haha, I love this, very ingenious! It solves my very special case from my toy problem above. The idea of reducing my issue to a toy problem was to just highlight the fact that I don't know how to do member function specialisation on a single template parameter, whilst keeping the other parameters general. Vaughn's solution is closer to what I actually want, except that it requires specialising the entire class, rather than just a couple of functions. – Dave Dec 31 '12 at 18:20
5

Here is how your example might work. You first declare your primary template:

template <typename T, int d> class Point;

There is no need to define it, because you don't have a general implementation.

Next, you create partial specializations for different numbers of dimensions, but your partial specializations will still have type T as a template parameter:

template <typename T>
class Point<T,2>
{
public:
    T x;
    T y;

    T add()
    {
        return x + y;
    }
};

template <typename T>
class Point<T,3>
{
public:
    T x;
    T y;
    T z;

    T add()
    {
        return x + y + z;
    }
};
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • OK, only problem is if I want to have general-purpose functions alongside the specialised (or overriden) functions, how could I do that? I don't want to specialise an entire class, when only a couple of functions are actually different. – Dave Dec 31 '12 at 18:13
  • @Dave: One way to do that is to have a derived class that has the generic functions which is not specialized, and have it inherit from a base class which has specializations. – Vaughn Cato Dec 31 '12 at 20:56
  • I suspected I might have to resort to subclassing rather than template specialisation. OK, then, I think I have my answer. – Dave Jan 01 '13 at 01:31