【问题标题】:unique_ptr's of zero sizeunique_ptr 的大小为零
【发布时间】:2017-03-03 03:01:45
【问题描述】:

我经常使用多维数组,我不是std::vector 的忠实粉丝,因为不可能使用不带引用的std::vector 实例化std::vectorstd::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](), [&](T** x) {
                                              if (malloc_usable_size(x) > 0) {
                                                _mm_free(&(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] = &(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);

应该返回一个带有行和列指针的 3 维数组。

问题: 1)避免以简单的方式读取无效数据。 2) 使用模板参数D 生成类型:T*T** 并简单地传递D 以递归生成行指针(我已经拥有)。 3) 最好以便携的方式。 malloc_usable_size 是一个 GNU 扩展,当大小为 0 时,在 x 上调用它会导致读取无效。

提前致谢

【问题讨论】:

  • "std::vector 或 std::vector 的 std::vector 使用引用而不复制基础数据" 你是什么意思?
  • 实现多维数组的一个好方法是将数组展平并“手动”进行索引计算。
  • 如果你想使用 std::vector 作为参数类型来表示一个函数,你需要创建一个实例,这会发出一个副本。我经常使用多维数组的子数组,想避免复制。
  • 我可以使用 llvm 的 ArrayRef 但它需要 llvm。
  • 不,索引是有风险的。一个小小的错字可能会导致错误,一旦你使用 3 维数组的子数组,它就会变得复杂

标签: c++ arrays c++11 unique-ptr


【解决方案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](),
                                    [&](T** x) {
                                      if (sps::msize(x) > 0) {
                                        _mm_free(&(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] = &(data.get()[0][iRow*n]);
      }
    }
    else {
      data.reset();
    }
  }

  deleted_aligned_multi_array(deleted_aligned_multi_array&& other) : m(other.m), n(other.n),
                                 data(std::move(other.data)) {}

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

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

  T* operator[](size_t m) {
    return &(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_array& other) = delete;
  deleted_aligned_multi_array& operator=( const deleted_aligned_multi_array& 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.
}

【讨论】:

    猜你喜欢
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 2012-06-26
    相关资源
    最近更新 更多