unique_ptr нулевого размера

#c #arrays #c 11 #unique-ptr

#c #массивы #c 11 #уникальный-ptr

Вопрос:

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

Для одномерных массивов я использую следующее

 template<typename T>
using deleted_aligned_array = std::unique_ptr<T[], std::function<void(T*)> >;

template<typename T>
deleted_aligned_array<T> deleted_aligned_array_create(size_t n) {
  return deleted_aligned_array<T>((T*)_mm_malloc(n*sizeof(T),16), [](T* f)->void { _mm_free(f);});
}
  

Это очень удобно и позволяет мне создавать экземпляры массива динамически изменяемого размера, который также работает для нулевого размера. Кроме того, я могу использовать std::forward для передачи данных без копирования.

Для двумерного массива я хотел бы сделать что-то вроде

 template<typename T>
using deleted_aligned_array2 = std::unique_ptr<T*,std::function<void(T**)>>;

template<typename T>
deleted_aligned_array2<T> deleted_aligned_array_create(size_t m, size_t n) {
  auto arr = deleted_aligned_array2(new T*[m](), [amp;](T** x) {
                                              if (malloc_usable_size(x) > 0) {
                                                _mm_free(amp;(x[0][0]));
                                              }
                                              delete[] x;});
  if (m*n > 0) {
    arr.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16);

    // Row pointers
    for (size_t iRow = 1; iRow < m; iRow  ) {
      (m_data.get())[iRow] = amp;(m_data.get()[0][iRow*n]);
    }
  }
  return arr;
}
  

Это работает для массивов нулевого размера, но я получаю сообщение об ошибке от valgrind по очевидным причинам, invalid read of size 8 .

Возможно ли решить это элегантным способом, не создавая целый класс, сохраняющий std::unique_ptr член, где я реализую конструктор перемещения, назначение перемещения и т.д. В конечном счете, я хотел бы обобщить это, чтобы использовать для любого измерения

 template<typename T, size_t Dim>
deleted_aligned_array<T,D> deleted_aligned_array_create(...);
  

Возвращаемый массив должен быть уникальным указателем с рекурсивно инициализированным указателем на строку, и он должен поддерживать массивы нулевого размера, например

 auto arr = deleted_aligned_array_create<float,3>(4,5,10);
  

должен возвращать трехмерный массив с указателями на строки и столбцы.

Проблемы: 1) Избегайте чтения недопустимых данных простым способом. 2) Используйте параметр шаблона D для генерации типов: T* , T** и просто передайте D коду, рекурсивно генерирующему указатели на строки (это у меня уже есть). 3) Предпочтительно переносимым способом. malloc_usable_size это расширение GNU, и его вызов x приводит к недопустимому чтению, когда размер равен 0.

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

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

1. «std::vector или std::vector std::vector использует ссылку без копирования базовых данных» Что вы имеете в виду?

2. Хороший способ реализовать многомерный массив — это сгладить массив и выполнить вычисление индекса «вручную».

3. Если вы хотите использовать std::vector в качестве типа аргумента, скажем, для функции, вам нужно создать экземпляр, и это приведет к созданию копии. Я часто использую подмассивы многомерных массивов и хочу избежать копирования.

4. Я мог бы использовать ArrayRef из llvm, но для этого требуется llvm.

5. Нет, индексирование сопряжено с риском. Небольшая опечатка может привести к ошибкам, и как только вы работаете с подмассивами трехмерного массива, это усложняется

Ответ №1:

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

 template <class T, size_t D>
class deleted_aligned_multi_array {
};

template <class T>
class deleted_aligned_multi_array<T,1> : public std::unique_ptr<T[], std::function<void(T*)> > {
  deleted_aligned_multi_array(size_t n) :
    std::unique_ptr<T[], std::function<void(T*)> >((T*)_mm_malloc(n*sizeof(T),16),
                                                   [](T* f)->void { _mm_free(f);}) {}
};

template <class T>
class deleted_aligned_multi_array<T,2> {
public:
  typedef T** pointer;
  typedef std::unique_ptr<T*, std::function<void(T**)>> deleted_unique_array;

  deleted_aligned_multi_array() : m(0), n(0), data() {}

  deleted_aligned_multi_array(size_t m, size_t n) : m(m), n(n) {
    if (m*n > 0) {
      data = deleted_unique_array(new T*[m](),
                                    [amp;](T** x) {
                                      if (sps::msize(x) > 0) {
                                        _mm_free(amp;(x[0][0]));
                                      }
                                      delete[] x;});

      data.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16);

      for (size_t iRow = 1; iRow < m; iRow  ) {
        (data.get())[iRow] = amp;(data.get()[0][iRow*n]);
      }
    }
    else {
      data.reset();
    }
  }

  deleted_aligned_multi_array(deleted_aligned_multi_arrayamp;amp; other) : m(other.m), n(other.n),
                                 data(std::move(other.data)) {}

  deleted_aligned_multi_arrayamp; operator=( deleted_aligned_multi_arrayamp;amp; other ) {
    if (this != amp;other) {
      data = std::move( other.data );
      m    = other.m;
      m    = other.n;
    }
    return *this;
  }

  Tamp; operator()(size_t i, size_t j) {
    return this->data.get()[0][i*n   j];
  }

  T* operator[](size_t m) {
    return amp;(data.get()[m][0]);
  }

  const T* operator[](size_t m) const {
    return data.get()[m];
  }

  pointer get() const {
    return data.get();
  }

  void reset(pointer __p = pointer()) {
    data.reset(__p);
  }

  template<typename _Up>
  void reset(_Up) = delete;

private:
  deleted_aligned_multi_array(const deleted_aligned_multi_arrayamp; other) = delete;
  deleted_aligned_multi_arrayamp; operator=( const deleted_aligned_multi_arrayamp; a ) = delete;

public:
  size_t m;                   ///<Number of rows
  size_t n;                   ///<Number of columns
  deleted_unique_array data;  ///<Data
};
  

Теперь можно легко создать служебную функцию для доступа к подмассиву

 template <class T>
std::unique_ptr<T*, std::function<void(T*)> sub_array(size_t m, size_t n, size_t i, size_t j) {
  // Establish row pointers with reference i and j and dimension mxn.
}