2

How do I achieve the following:

std::vector<int> vec = { 1, 2, 3 };
const int N = vec.size();
// Now create NxN 2D array.

First, I know I could do it with new but I'd have to remember to delete it later, and I'd rather not have to handle deallocation of memory if possible.

Second, I can't declare the 2D array on the stack because N is not (and can't be in this case) a constant expression. (In any case I'm using VS2013 and it doesn't support constexpr.)

Third, I can't (or maybe don't know how to) use std::array because apparently "a local variable cannot be used as a non-type argument". (I copy-pasted this from the VS2013 compile dialogue and have little understanding regarding this point).

Fourth, I'm thinking of using unique_ptr. The problem is, I know how to use unique_ptr for a 1D array, like std::unique_ptr<int> arr{ new int[N] }, but can't figure out how to do it for a 2D array.

Lastly, I know I can always write my own thin wrapper around a C-style array that's always created on the heap, or write my own 2D array class. But is there a native or standard library way of doing this in C++ (C++11)?

Drax
  • 12,682
  • 7
  • 45
  • 85
Ray
  • 7,833
  • 13
  • 57
  • 91
  • `vector> my2DArray(N, vector(N));` – Igor Tandetnik Apr 06 '15 at 14:06
  • 2
    I suggest writing a 2D array class, with a single vector as data storage. You can provide an `int& operator()(size_t row, size_t col);` for 2d element access. – juanchopanza Apr 06 '15 at 14:07
  • 1
    @Igor I'd rather have a 2D array rather than a 2D vector. – Ray Apr 06 '15 at 14:07
  • 1
    `I'd rather have a 2D array rather than a 2D vector.` Why? Do you have additional requirements you neglected to mention, that a vector of vectors fails to satisfy? – Igor Tandetnik Apr 06 '15 at 14:08
  • Hmm I suppose you're right, it's just that I've so far only imagined the use-case for my problem in terms of arrays (because I don't need to change the size and I know exactly how much space to allocate from the outset). I suppose it could work with `std::vector`s. It's still interesting to me to see if there is a simple solution with arrays though, I learn more about C++ that way. – Ray Apr 06 '15 at 14:15
  • 1
    @Ray If you need the data to be contiguous then a vector of vectors won't do. Besides indirection, there is also a size overhead for each vector. It is worth keeping this in mind, because it may matter depending on the use-case. – juanchopanza Apr 06 '15 at 14:19
  • 2
    Just FWIW, a [2D vector wrapper](http://stackoverflow.com/a/12009991/179910). – Jerry Coffin Apr 06 '15 at 14:20
  • @juanchopanza, regarding your first comment, I tried to do `operator[](size_t i, size_t j)` and it doesn't work because it "does not define this operator or a conversion to a type acceptable to the predefined operator". I don't fully understand this but is there a way to make indexing work with `[]` instead of `()`? – Ray Apr 06 '15 at 15:56
  • 2
    @Ray `operator[]` can only take one index, so you're stuck with `operator()(size_t row, size_t col);`. I think that's fine. – juanchopanza Apr 06 '15 at 15:58
  • "But is there a native or standard library way of doing this in C++ (C++11)?" Unfortunately, the answer is no. Writing your own 2d array class (or using one somebody has previously in put in a library) is the right thing to do. – bames53 Apr 08 '15 at 01:00
  • @juanchopanza It's true that [] only takes one argument, but you can still use it. My answer [here](http://stackoverflow.com/a/29055366/365496) shows an example. I also use this in a more complicated [proof of concept](https://gist.github.com/bames53/3d23426afbf47dc62514). – bames53 Apr 08 '15 at 01:02

4 Answers4

2

std::experimental::array_view is a view to an n-dimensional array with dynamic size bounds on a packed buffer.

So one approach is to create a contiguous buffer (say, a std::vector<T>, or a std::unique_ptr<T[]>, and then wrap an array_view<T,2> around it.

Access through the view object, and it will have the operations you should expect from an array. The storage is managed separately from the way of looking at the storage.

Writing a simplified version of this array_view for the 1 and 2 dimensional case isn't tricky. But the result is your code is high performance, and very clear at point of use. The glue code (for array_view) can be a bit tricky, but once tested it should be solid: and the likelihood that a similar construct will be added to std shortly means it won't remain obscure for long.

In my experience, once I have a solid array_view type, I use it as a drop-in replacement for where I was (inefficiently) using std::vector to pass bundles of data around in the past.

If you want to write your own, I'd skip the bits about bounds and indexes, and just implement slicing -- [] on a 2nd dimension array_view returns a 1st dimension array_view, and [] on a 1st dimension array_view returns a T&.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

I suggest you write a class for it.

Example below: set() resizes it before setting values. The operator [] returns the column vector for that row, so when you apply the operator [] it returns the desired value. Let me know if you find any issue ;).

class 2DVector {
 std::vector<std::vector<int>> m_items;

 void set(int value, size_t row, size_t column) {
   for (int i=m_items.size(); i<=row; i++) {
      m_items.push_back(std::vector<int>());
   }
   for (int i=0; i<m_items.size(); i++) {
      for (int j=m_items[i].size(); j<=column; j++) {
      m_items[i].push_back(0);
   }
   m_items[row][column] = value;
 }

 std::vector<int> &operator [](size_t index) {
   return m_items[index];
 }
}

Usage:

2DVector v;
v.set(200, 0, 0);
v.set(201, 1, 0);
std::cout << v[0][0]; //prints 200
std::cout << v[1][0]; //prints 201
Pedro Israel
  • 119
  • 5
  • 1
    This creates a jagged array (an array of "unconnected" arrays), which is often a bad idea for performance reasons. – Yakk - Adam Nevraumont Apr 06 '15 at 19:07
  • Agree @Yakk. There is always a tradeoff between usability / readability vs performance. That's a decision that Ray will have to take and change the implementation and class interface accordingly. – Pedro Israel Apr 06 '15 at 19:19
  • 1
    The only reason to use `vector>` is because you can just use it directly without defining anything else. Since you're wrapping the type in a class anyway I'd say just do it [right](http://stackoverflow.com/questions/29054812/how-to-create-a-two-dimensional-array-of-given-size-in-c/29055366#29055366); Doing it right produces a class that's no less usable or readable. – bames53 Apr 08 '15 at 01:08
0

The standard library way of doing this is:

std::vector< std::vector<int> > vec2d (vec.size(), vec);

which will initialize each row with the values from vec. If you don't want to do this then leave off the final argument.

M.M
  • 138,810
  • 21
  • 208
  • 365
-1

What about mimic a 2D array by 1D array?Like the way done by openCV2

pseudo codes

class 2DVector {         
 void set(int value, size_t row, size_t column) {
   m_items[row * column_size + column];
 }

 int &operator [](size_t row, size_t column) {
   return m_items[row * column_size + column];
 }

 private:
   std::vector<int> m_items;
}

Or just use the boost::multi_array(Not sure about the performance is good enough for your case).

user3689849
  • 521
  • 1
  • 3
  • 14
  • I think the `int& operator [](size_t row, size_t column)` doesn't work. See the last couple of comments on my question. I do like the idea of mimicking a 2D array by 1D array though. – Ray Apr 07 '15 at 18:36
  • Yeah, you can't have two arguments to [] like this (unfortunately), but you can do it like I show in [this](http://stackoverflow.com/questions/29054812/how-to-create-a-two-dimensional-array-of-given-size-in-c/29055366#29055366) example, which is almost as good. – bames53 Apr 08 '15 at 01:10