Класс шаблонов с определенной размерностью массива

#c #arrays #class #templates #types

Вопрос:

Я в основном пытаюсь создать Container класс, который работает с уже заданным Data классом. Я пытаюсь добиться того, чтобы класс инициализировался с разным количеством указателей (размерностью) в зависимости от шаблона, который я передаю (показано ниже). Ключ здесь Data в том, что класс является внешним, я не могу его изменить.

 // this is just for the demo (I cannot change this class, it's from an external library):
template<class T>
class Data {
  T m_data;
};

enum Dimension { ONE_D, TWO_D, THREE_D };

// this is the class I'm trying to make:
template<class T, Dimension D>
class Container {
  Data<T*> data; // <- for ONE_D
  Data<T**> data; // <- for TWO_D
  Data<T***> data; // <- for THREE_D
};

// desired example usage
Container<float, ONE_D> mycontainer;
 

Я был в некотором роде в состоянии добиться этого довольно уродливым способом:

 template <typename T>
struct ONE_D {
  using ndtype_t = T*;
};

template <typename T>
struct TWO_D {
  using ndtype_t = T**;
};

template <typename T>
struct THREE_D {
  using ndtype_t = T***;
};

template <class U, template <typename T> class D>
class Container {
  Data<typename D<U>::ndtype_t> data;
};


Container<float, ONE_D> mycontainer;
 

Но мне было интересно, есть ли более элегантное решение этой проблемы.

Заранее спасибо.

Комментарии:

1. Для меня это пахнет проблемой XY , и обобщение зашло слишком далеко. Зачем вам нужен один класс, чтобы справиться со всеми тремя случаями? Каковы варианты использования этого контейнера? Какова основная проблема, которую он должен решить, которую вы не можете решить с помощью простого массива массивов или векторов векторов?

2. Это действительно может быть XY, но маловероятно, так как я много думал об этом. Короче говоря, этот Data класс представляет собой объект типа массива с перегруженными операторами индексирования (i,j,k) , которые могут быть скомпилированы как на GPU, так и на CPU и использовать simd обработку на различных микроархитектурах. Самое замечательное в нем то, что он обрабатывает все различия в архитектурах (avx2 против avx512, циклическое кэширование против объединения, распараллеливание и т. Д.) Внутри, Что имеет решающее значение, если вы тратите 1e6 процессорных часов на умножение массивов и матриц.

3. Причина, по которой мне нужен один класс контейнера, заключается в том, что у меня будут методы, общие для всех измерений: чтение из/запись в файл, скалярное умножение Data A Data B и т. Д. Теперь в принципе я мог бы Container2D это сделать и т. Container3D Д. , Но тогда мне пришлось бы переписать все эти процедуры для каждого из классов.

4. Наследование, где вы помещаете общий код в базовый класс? Кроме того, попытка втиснуть все в один класс, скорее всего, усложнит обслуживание кода.

5. Почему в массиве multi-D используется указатель на указатель? Это пахнет рваным массивом, что в некоторых случаях является худшей реализацией из-за проблем с локальностью?

Ответ №1:

То, что я в итоге сделал, было немного нетрадиционно, но я думаю, что это более правильное решение, чем то, что я делал раньше.

 template <typename T, Dimension D>
using RealND = typename std::conditional<D == ONE_D,
                      T*,
                  typename std::conditional<D == TWO_D,
                      T**,
                  typename std::conditional<D == THREE_D,
                      T***,
                    std::nullptr_t>::type>::type>::type;

template<class T, Dimension D>
class Container {
  Data<RealND<T,D>> data;
};