在模板特化中使用非类型模板模板参数

Using non-type template template parameter in template specialisation

我正在学习模板元编程,但在尝试实现某些目标时遇到了障碍 我认为这应该是可能的,我只是完全不知道该怎么做。

我找到了这两个我认为有些相关但并没有真正帮助的答案

假设我想表示一维和二维的点 space,那么我可以按如下方式定义它们:

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;
};

现在我想计算两点之间的距离。一种选择是这样做:

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

然而,这将接受 Point2D 和定义 ::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

现在,应该可以为 Point1D 和 Point2D 类型专门化 Distance,但这是我遇到困难的地方。我尝试了以下方法:

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 */
};

但当然现在我专注于 class 模板,而不是类型,所以这行不通。我可以将非类型参数传递给专业化:

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

但这违背了目的,因为我只能通过 5 和 2 的距离。我真正需要做的是能够传递任何采用一个 int N 并具有成员 X 的 class 模板,以便我可以使用任何 Point1D<N> 调用 Distance。例如。像这样:

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;
};

从概念上讲,我认为这应该可行,因为在调用 Distance 时,类型包含所有需要的信息。不知何故,通过指定 Distance 采用模板 Point1D<int N>,我需要该函数能够调用 Point1D<N>::x。语法是我无法弄清楚的。

如果我对问题的理解正确,解决方案是声明模板类型 Distance 但不定义它,并且仅部分专门化每对可接受的类型。例如:

template <typename, typename>
struct Distance;

由于此模板类型没有定义,尝试用任意类型实例化它都会失败,除非类型参数匹配稍后定义的部分特化。

现在部分专注于 Point1D:

template <int ...A, int ...B>
struct Distance<Point1D<A...>, Point1D<B...>> {
    static constexpr int value = Point1D<B...>::x - Point1D<A...>::x;
};

您可以通过使用相同的技术并根据需要更改计算,将其部分专门用于 Point2D

template <int ...A, int ...B>
struct Distance<Point2D<A...>, Point2D<B...>> {
    static constexpr int value = /* ... */;
};

(请注意,int 参数包在技术上是不必要的,因为您知道每种类型使用了多少参数。但是,使用包使偏特化定义彼此保持一致 使得代码更易于维护。它还强制计算使用值 Point_D<...>::x 而不是仅使用模板参数的值。)

What I really need to do is able to pass any class template that takes one int N and has a member X

为什么,你最后的尝试不错:

// Declare `Distance` as a template that takes two type parameters.
template <typename A, typename B>
struct Distance;

// Specialize 'Distance' for a special case where both type parameters
// are instances of the same template class.
template <template <int> typename AnyTemplate, int N, int M>
struct Distance<AnyTemplate<N>, AnyTemplate<M>>
{
    static constexpr int value = AnyTemplate<N>::x - AnyTemplate<M>::x;
};