简体   繁体   中英

C++11 dynamically allocated variable length multidimensional array

I've been trying to create a variable length multidimensional array. As I understand, you can't create variable length arrays on the stack, but you can create 1D variable length arrays in C++ using dynamic allocation. Correct me if this is a compiler extension, but it seems to work fine on clang and gcc with --pedantic set.

int size = 10;
int *ary = new int[size]();

I tried to extend the concept to multidimensional arrays. Here are my results. The problem with possiblity 1 and 2 is that they require a constexpr and do not work with variable sizes. Is it possible to make either of them accept a variable as its size? I put possibility 3 as I am aware of it, but it lacks [][] access, which is what I'm looking for.

constexpr int constSize = 10;

//Possibility 1: Only works in C++11
//Creates CONTIGUOUS 2D array equivalent to array[n*n], but with [][] access

int (*ary1)[constSize] = new int[constSize][constSize]();
delete [] ary1;

//Possibility 2:
//Really horrible as it does NOT create a contiguous 2D array
//Instead creates n seperate arrays that are each themselves contiguous
//Also requires a lot of deletes, quite messy

int **ary2 = new int*[constSize];
for (int i = 0; i < n; ++i)
    ary2[i] = new int[constSize];
for (int i = 0; i < n; ++i)
    delete [] ary2;
delete [] ary2;

//Possibility 3:
//This DOES work with non-constexpr variable
//However it does not offer [][] access, need to access element using ary[i*n+j]

int *ary3 = new int[size*size];
delete [] ary3;

This will create a dynamically allocated 2D variable-length array, with dimensions w and h :

std::vector<std::vector<int>> ary4(w, std::vector<int>(h));

It can be accessed with [][] :

ary4[x][y] = 0;

However, it's not contiguously allocated. To get a contiguous array, here's one solution:

template<typename E>
class Contiguous2DArray
{
public:
    Contiguous2DArray(std::size_t width, std::size_t height)
    : array(width * height), width(width) {}

    E& operator()(std::size_t x, std::size_t y)
    { return array[x + width * y]; }

private:
    std::vector<E> array;
    std::size_t width;
}

It can be used like this:

Contiguous2DArray<int> ary5(w, h);
ary5(x, y) = 0;

The number of dimensions is fixed, because the type of what [] returns changes based on the number of dimensions. Access is through both repeated [] and (...) . The first mimics C-style array lookup. The (...) syntax must be complete (it must pass N args to an N dimensional array). There is a modest efficiency cost to support both.

Uses C++14 features to save on verbosity. None are essential.

Using an an n_dim_array with 0 dimensions will give bad results.

template<class T, size_t N>
struct array_index {
  size_t const* dimensions;
  size_t offset;
  T* buffer;
  array_index<T,N-1> operator[](size_t i)&&{
    return {dimensions+1, (offset+i)* *dimensions, buffer};
  }
  template<class...Is, std::enable_if_t<sizeof...(Is) == N>>
  T& operator()(size_t i, Is...is)&&{
    return std::move(*this)[i](is...);
  }
};
template<class T>
struct array_index<T,0> {
  size_t const* dimension;
  size_t offset;
  T* buffer;
  T& operator[](size_t i)&&{
    return buffer[i+offset];
  }
  T& operator()(size_t i)&&{
    return std::move(*this)[i];
  }
};

template<class T, size_t N>
struct n_dim_array {
  template<class...Szs, class=std::enable_if_t<sizeof...(Szs)==N>>
  explicit n_dim_array( Szs... sizes ):
    szs{ { static_cast<size_t>(sizes)... } }
  {
    size_t sz = 1;
    for( size_t s : szs )
      sz *= s;
    buffer.resize(sz);
  }
  n_dim_array( n_dim_array const& o ) = default;
  n_dim_array& operator=( n_dim_array const& o ) = default;


  using top_level_index = array_index<T,N-1>;
  top_level_index index(){return {szs.data(),0,buffer.data()};}
  auto operator[]( size_t i ) {
    return index()[i];
  }
  using const_top_level_index = array_index<const T,N-1>;
  const_top_level_index index()const{return {szs.data(),0,buffer.data()};}
  auto operator[]( size_t i ) const {
    return index()[i];
  }
  template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
  T& operator()(Is...is){
    return index()(is...);
  }
  template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
  T const& operator()(Is...is) const {
    return index()(is...);
  }
private:
  n_dim_array() = delete;
  std::array<size_t,N> szs;
  std::vector<T> buffer;
};

live example

Does not support for(:) loop iteration. Writing an iterator isn't that hard: I'd do it in array_index .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM