Использование нетипичного параметра шаблона шаблона в специализации шаблона - PullRequest
1 голос
/ 15 октября 2019

Я узнаю о шаблонном метапрограммировании и столкнулся с проблемой, пытаясь достичь чего-то, что, по моему мнению, должно быть возможным, я просто не могу понять, как это сделать.

Я нашел эти два ответакоторые, как я думал, были в некоторой степени связаны, но не помогли

Допустим, я хочу представить точки в 1D и 2D пространстве, тогда я могу определить их следующим образом:

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

Теперь должна быть возможность специализировать Distance для Point1D. и Point2D, но я застрял здесь. Я попробовал следующее:

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

Но, конечно, сейчас я специализируюсь на шаблоне класса, а не на типе, так что это не будет работать. Я могу передать нетипизированные параметры специализации как таковые:

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, так что я могу вызвать Distance с любым Point1D<N>. Например, что-то вроде этого:

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. Синтаксис - это то, что я не могу понять.

Ответы [ 2 ]

3 голосов
/ 15 октября 2019

Если я правильно понимаю вопрос, решение состоит в том, чтобы объявить тип шаблона 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 вместо использования значения аргумента шаблона.)

1 голос
/ 15 октября 2019

Что мне действительно нужно сделать, так это пропустить любой шаблон класса, который принимает одно целое число N и имеет член 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;
};
...