Вариативный шаблон шаблона

Я пытаюсь создать базовый класс, который является оболочкой вокруг std :: array, которая перегружает кучу общих арифметических операторов. Конечный результат будет похож на std :: valarray, но со статическим размером. Я делаю это, потому что создаю целый ряд дочерних классов для своей библиотеки, которые в конечном итоге воспроизводят эту функциональность. Например, мне нужно создать класс MyPixel и класс MyPoint, которые по сути представляют собой просто массивы статического размера, с которыми я могу выполнять арифметические операции.

Мое решение - создать базовый класс StaticValArray, от которого могут быть унаследованы MyPoint и MyPixel. Однако, чтобы запретить пользователям добавлять MyPoint в MyPixel, я использую шаблон CRTP как таковой:

template<class T1, class T2>
struct promote
{
  typedef T1 type; // Assume there is a useful type promotion mechanism here
};

template<class T, size_t S, template<typename... A> class ChildClass>
class StaticValArray : public std::array<T,S>
{
  public:
    // Assume there are some conversion, etc. constructors here...

    template<class U>
    StaticValArray<typename promote<T,U>::type,S,ChildClass> operator+ 
        (StaticValArray<U,S,ChildClass> const & rhs)
    {
      StaticValArray<typename promote<T,U>::type,S,ChildClass> ret = *this;
      std::transform(this->begin(), this->end(),
          rhs.begin(), ret.begin(), std::plus<typename promote<T,U>::type>());
      return ret;
    }


    // More operators....
};

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

template<class T, class U>
class MyClassTwoTypes : public StaticValArray<T,3,MyClassTwoTypes>
{ };

template<class T, class U>
class MyClassTwoTypes2 : public StaticValArray<T,3,MyClassTwoTypes2>
{ };

int main()
{
  MyClassTwoTypes<int, float> p;
  MyClassTwoTypes<double, char> q;
  auto z = p + q;

  MyClassTwoTypes2<double, char> r;
  //  r += q;  // <-- Great! This correctly won't compile

  return 0;
}

Моя проблема заключается в следующем: я хотел бы вставить немного ChildClass в бит CRTP StaticValArray, который не обязательно имеет только классы в качестве параметров шаблона. Например, рассмотрим этот класс N-Dimensional Point:

template<class T, size_t S>
class MyPointND : public StaticValArray<T,S,MyPointND>
{ };

К сожалению, это не скомпилируется, потому что size_t не является типом имени - я получаю ошибку компилятора:

type/value mismatch at argument 3 in template parameter list for ‘template<class T, long unsigned int S, template<class ... A> class ChildClass> class StaticValArray’
test.C:36:54: error:   expected a template of type ‘template<class ... A> class ChildClass’, got ‘template<class T, long unsigned int S> class MyPointND’

Есть ли способ создать пакет параметров шаблона вариативного шаблона, который может быть абсолютно любым (typenames, int, size_t, doubles, что угодно?), Потому что, в конце концов, мне действительно все равно, какой там тип. Обратите внимание, что я не могу просто полностью указать ChildClass (например, class MyPointND: public StaticValArray<T,S,MyPointND<T,S>>), потому что это нарушит мой механизм продвижения типа.


person rcv    schedule 15.04.2011    source источник
comment
Что касается структуры promote, вы можете использовать decltype: typedef decltype(T+U) type; внутри структуры.   -  person Matthieu M.    schedule 15.04.2011
comment
Моя реальная реализация продвижения действительно использует это, я просто называю это продвижением, чтобы быть понятным. Подробности здесь опущены для краткости.   -  person rcv    schedule 16.04.2011


Ответы (2)


Что, если бы вместо size_t вы использовали std :: integration_constant? Вы должны встроить в него числовое значение размера вашего массива и использовать его как тип.

ИЗМЕНИТЬ

Чтобы уменьшить многословие, вы можете определить свой собственный интегральный постоянный класс, например:

template <std::size_t N>
struct size_ : std::integral_constant<std::size_t,N> {};

Тогда вы могли бы использовать это так:

MyPointND<int,size_<3>> x;
person bluescarni    schedule 15.04.2011
comment
Хм, похоже, это в правильном направлении! К сожалению, создание экземпляра MyPointND становится немного болтливым: MyPointND ‹int, std :: integration_constant‹ int, 3 ›› x; - person rcv; 16.04.2011
comment
Что вы могли бы сделать, так это создать свой собственный класс, производный от std :: integration (отредактированный пост выше). - person bluescarni; 16.04.2011

Что вам нужно сделать, так это иметь класс traits, специализированный для каждого типа, содержащий все, что вам нужно для повышения типа, а затем передать полный тип в StaticValArray.

Более того, с decltype вам не нужно ничего подобного - decltype сообщит вам, что вы получите, добавив float и int.

template<class U>
StaticValArray<decltype(*(T*)nullptr + *(U*)nullptr),S,ChildClass> operator+ 
    (StaticValArray<U,S,ChildClass> const & rhs)
{
  StaticValArray<decltype(*(T*)nullptr + *(U*)nullptr),S,ChildClass> ret = *this;
  std::transform(this->begin(), this->end(),
      rhs.begin(), ret.begin(), std::plus<decltype(*(T*)nullptr + *(U*)nullptr)>());
  return ret;
}
person Puppy    schedule 15.04.2011
comment
Извините, я что-то упускаю - не могли бы вы рассказать немного о классе черт? - person rcv; 16.04.2011
comment
DeadMG: не было бы целесообразно использовать std :: declval вместо приведения и разыменования nullptr? - person bluescarni; 16.04.2011