Динамически распределяемый многомерный массив с использованием рекурсивных шаблонов

Чтобы прочитать и сохранить некоторые результаты из программы MATLAB, мне нужно использовать до 6-мерных матриц. Вместо того, чтобы делать что-то вроде:

typedef std::vector<double>  Row;
typedef std::vector<Row>     Matrix2;
typedef std::vector<Matrix2> Matrix3;
typedef std::vector<Matrix3> Matrix4;
typedef std::vector<Matrix4> Matrix5;
typedef std::vector<Matrix5> Matrix6;

Я решил использовать шаблоны, и вот что у меня есть на данный момент:

template <class T, int N>
class Matrix {
public:
    typedef typename Matrix<T, N - 1>::type MatrixOneDimLower;
    typedef std::vector<MatrixOneDimLower> type;

    type _data;

    template <unsigned int dn, typename ...NT>
    Matrix(unsigned int dn, NT ...drest) : _data(dn, MatrixOneDimLower(drest)) {}

    MatrixOneDimLower& operator[](unsigned int index)
    {
        return _data[index];
    }
};

template <class T>
class Matrix<T, 1> {
public:
    typedef std::vector<T> type;

    type _data;

    Matrix(unsigned int d0) : _data(d0, T(0.0)) {}

    T& operator[](unsigned int index)
    {
        return _data[index];
    }
};

К сожалению, я не очень разбираюсь в вариативных и рекурсивных шаблонах, и это не работает. Например, если я попытаюсь использовать это как:

Matrix<double, 4> temp(n,  dim[2], dim[1], dim[0]);

Я получаю эту ошибку времени компиляции (Visual Studio 2017):

error C2661: 'Matrix<double,4>::Matrix': no overloaded function takes 4 arguments

Буду очень признателен, если вы дадите мне знать, что я делаю не так.

Вам действительно не нужно много вложенных векторов. См. Нижнюю половину моего ответа здесь, чтобы узнать, почему, и игрушечный пример того, как обойти проблему.

NathanOliver 25.10.2018 21:16

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

user2486888 25.10.2018 21:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
template<class T, std::size_t I>
struct MatrixView {
  MatrixView<T, I-1> operator[](std::size_t i) {
    return {ptr + i* *strides, strides+1};
  }
  MatrixView( T* p, std::size_t const* stride ):ptr(p), strides(stride) {}
private:
  T* ptr = 0;
  std::size_t const* strides = 0;
};
template<class T>
struct MatrixView<T, 1> {
  T& operator[](std::size_t i) {
    return ptr[i];
  }
  MatrixView( T* p, std::size_t const* stride ):ptr(p) {}
private:
  T* ptr = 0;
};
template<class T, std::size_t N>
struct Matrix {
  Matrix( std::array<std::size_t, N> sizes ) {
    std::size_t accumulated = 1;
    for (std::size_t i = 1; i < sizes.size(); ++i) {
      accumulated *= sizes[N-i];
      strides[N-i] = accumulated;
    }
    storage.resize( strides[0] * sizes[0] );
  }
  MatrixView<T, N> get() { return {storage.data(), strides.data()}; }
  MatrixView<T const, N> get() const { return {storage.data(), strides.data()}; }
private:
  std::vector<T> storage;
  std::array<std::size_t, N-1> strides;
};

это требует выполнения Matrix<int, 6> m{ {5,4,2,1,3,5} }; для создания матрицы с 6 измерениями.

Для доступа к нему нужно сделать m.get()[3][0][0][0][0][0] = 4.

Вы избавитесь от этого .get(), но это немного раздражает, если вы хотите поддерживать тензоры первого порядка.

Данные хранятся непрерывно.

Большое спасибо! Это очень полезно. Однако у меня есть вопрос, как я могу изменить это, чтобы шаги не были константами времени компиляции. Потому что я не знаю шагов для каждого измерения, пока не загружу файлы. Итак, я хочу иметь возможность писать Matrix<int, 2> a { {d0, d1} }, где d0 и d1 - переменные времени выполнения.

triple_r 25.10.2018 21:58

@triple_r Размеры - это просто значения в std::array. Успехи - продукт размеров. На 1 шаг меньше, чем размер, так как измерение верхнего уровня имеет значение только для размера буфера, а не для расстояния между элементами.

Yakk - Adam Nevraumont 25.10.2018 21:59

Ой, извините, я имел ввиду размер габаритов. Мне нужно было преобразовать свои переменные в size_t, иначе я получал ошибку :-) Еще раз спасибо.

triple_r 25.10.2018 22:06

Другие вопросы по теме