I am learning about template metaprogramming and I have hit a snag trying to achieve something which I think should be possible, I just quite can't figure out how to do it.
I found these two answers that I thought were somewhat related but didn't really help
Let's say I want to represent points in 1D and 2D space, then I can define these as follows:
template <int X>
struct Point1D {
static constexpr int x = X;
};
template <int X, int Y>
struct Point2D {
static constexpr int x = X;
static constexpr int y = Y;
};
Now I want to calculate the distance between two points. One option is to do this:
template <typename Point1, typename Point2>
struct Distance {
static constexpr int value = Point2::x - Point1::x;
};
typedef Distance<Point1D<1>, Point1D<5>> dist;
dist::value; //4
However this will accept both Point2D and ANY type that defines ::x
template <int X>
struct RandomType {
static constexpr int x = X;
};
typedef Distance<Point2D<5, 4>, Point1D<2>> dist2; //Should not work
typedef Distance<Point1D<5>, RandomType<6>> dist3; //Should not work either
Now, it should be possible to specialise Distance for Point1D and Point2D types, however this is where I get stuck. I tried the following:
template <template <int...> typename Point1, template <int...> typename Point2>
struct Distance {};
template <>
struct Distance<Point1D, Point1D> {
/* Calculate distance between two points */
};
template <>
struct Distance<Point2D, Point2D> {
/* Calculate distance between two points in 2D space */
};
But of course now I am specialising for a class template, not a type, so this won't work. I can pass the non-type parameters to the specialisation as such:
template <template <int...> typename Point1, int N,
template <int...> typename Point2, int M>
struct Distance {};
template <int N, int M>
struct Distance<Point1D, N, Point1D, M> {
static constexpr int value = Point1D<M>::x - Point1D<N>::x;
};
typedef Distance<Point1D, 2, Point1D, 5> Two;
int x = Two::value; //3
But this defeats the purpose as I could just pass 5 and 2 for the distance. What I really need to do is able to pass any class template that takes one int N and has a member X so that I can call Distance with any Point1D<N>
. E.g. something like this:
typedef Point1D<4> p1;
typedef Point1D<6> p2;
Distance<p1, p2>::value; //2
typedef Point2D<4, 6> p3;
Distance<p3, p1>::value //Error, can't mix Point1D and Point2D
/* Something of the sort (doesn't compile) */
template<int N, int M>
struct Distance<Point1D<N>, Point1D<M>> {
static constexpr int value = Point1D<N>::x - Point1D<M>::x;
};
Conceptually I think this should work since at the point of the call to Distance the type contains all the information needed. Somehow, by specifying that Distance takes a template Point1D<int N>
I need the function to be able to call Point1D<N>::x
. The syntax is what I am unable to figure out.