#c #templates #c 11
#c #шаблоны #c 11
Вопрос:
Я пытаюсь создать базовый класс, который является оболочкой вокруг 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 amp; 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....
};
Это довольно круто, потому что дочерний класс может иметь любые произвольные параметры шаблона класса, и эта штука будет работать. Например:
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;
}
Моя проблема заключается в следующем: я хотел бы вставить некоторый дочерний класс в бит CRTP StaticValArray, который не обязательно содержит только классы в качестве параметров шаблона. Для примера рассмотрим этот N-мерный класс точек:
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’
Есть ли какой-либо способ создать пакет параметров шаблона variadic template, который может быть абсолютно любым (имена типов, целые числа, size_t, doubles, что угодно?) потому что, в конце концов, мне действительно все равно, какой там тип. Обратите внимание, что я не могу просто полностью указать дочерний класс (например class MyPointND: public StaticValArray<T,S,MyPointND<T,S>>
), потому что это нарушило бы мой механизм продвижения типа.
Комментарии:
1. Что касается
promote
структуры, вы можете воспользоваться преимуществамиdecltype
:typedef decltype(T U) type;
внутри структуры.2. Моя реальная реализация продвижения использует это, я просто называю это «продвигать», чтобы было понятно. Подробности здесь опущены просто для краткости.
Ответ №1:
Что, если бы вместо size_t вы использовали std::integral_constant? Вы бы вставили в него числовое значение размера вашего массива и могли бы использовать его в качестве типа.
Редактировать
Чтобы уменьшить детализацию, вы могли бы определить свой собственный класс integral constant, что-то вроде:
template <std::size_t N>
struct size_ : std::integral_constant<std::size_t,N> {};
Тогда вы могли бы использовать его следующим образом:
MyPointND<int,size_<3>> x;
Комментарии:
1. Хм, кажется, это в правильном направлении! К сожалению, создание экземпляра MyPointND становится немного болтливым: MyPointND<int, std::integral_constant<int,3>> x;
2. Что вы могли бы сделать, так это создать свой собственный класс, производный от std::integral (отредактированный пост выше).
Ответ №2:
Что вам нужно сделать, так это иметь traits
класс, специализированный для каждого типа, содержащий все, что вам нужно для расширения типа, а затем передать полный тип в StaticValArray.
Более того, с decltype
вам не должно понадобиться ничего подобного — decltype
скажет вам, что вы получите, добавив float и int.
template<class U>
StaticValArray<decltype(*(T*)nullptr *(U*)nullptr),S,ChildClass> operator
(StaticValArray<U,S,ChildClass> const amp; 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;
}
Комментарии:
1. Извините, я, должно быть, что-то упускаю — не могли бы вы немного рассказать о классе traits?
2. DeadMG: не было бы подходящим использовать std::declval вместо приведения и разыменования nullptr?