Access row in vector<vector<int>>

I'm stuck on an innocuous problem that just isn't going away.

Consider this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Trollop
{
  vector<vector<int>> M;

  public:
    Trollop::Trollop(unsigned NM)
    {  // Some NM
       M.reserve(NM);
    }

    void method(int new_d, unsigned k)
    {
      vector<int> *A = &M[k];
      A->emplace_back(new_d);
    }
};
I'm looking to add new data (new_d) into the (k)-vector slot of the matrix M. The code compiles but I get a SIGSEGV followed by a crash.

I'm not understanding the proper usage of vector<vector>>. If I use array<vector> where the array is predetermined and fixed then this works.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <vector>

struct trollop
{
    std::vector< std::vector<int> > mtx ;

    trollop( std::size_t nrows )
    {
        mtx.resize(nrows) ; // note: resize
        std::cout << "troll::mtx has " << nrows << " rows\n" ;
    }

    bool append( int new_d, std::size_t row )
    {
        if( row < mtx.size() )
        {
            mtx[row].push_back(new_d) ;
            return true ;
        }

        else return false ; // out of range
    }
};

int main()
{
     trollop troll(12) ; // 12 rows

     if( troll.append( 1234, 8 ) ) std::cout << "1234 appended to row 8\n" ;

     if( troll.append( 5678, 21 ) ) ; // 21 is out of range
     else std::cout << "invalid row 21 (out of range)\n" ;
}
If you don't create a 2D vector with known row and column sizes, you have to push back each row.

Create a temp vector with the column elements for each row, push_back that temp to add the new row.

You should use push_back with an already created temp vector, emplace/emplace_back creates a vector in-place.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vec2d;

   std::cout << "The number of rows in vec2d is: " <<vec2d.size() << "\n\n";

   // create a temp vector
   std::vector<int> temp { 1, 2, 3, 4, 5 };

   // and push back the temp
   vec2d.push_back(temp);

   // emplace creates a unnamed temp vector in-place
   vec2d.emplace_back(std::vector<int> { 6, 7, 8, 9, 10});

   std::cout << "The number of rows in vec2d is: " << vec2d.size() << "\n\n";

   for (size_t rows = 0; rows < vec2d.size(); rows++)
   {
      // operator[] does no bounds checking
      for (size_t cols = 0; cols < vec2d[rows].size(); cols++)
      {
         // vector's at operator does bounds checking
         std::cout << vec2d.at(rows).at(cols) << '\t';
      }
      std::cout << '\n';
   }

   std::cout << "\nAdding new elements to the first row.....\n";

   vec2d.at(0).push_back(25);

   std::cout << "\nLet's see the 2D vector contents again:\n\n";

   // range based for loops make it harder to go out of bounds
   for (const auto& rows : vec2d)
   {
      for (const auto& cols : rows)
      {
         std::cout << cols << '\t';
      }
      std::cout << '\n';
   }
}

The number of rows in vec2d is: 0

The number of rows in vec2d is: 2

1       2       3       4       5
6       7       8       9       10

Adding new elements to the first row.....

Let's see the 2D vector contents again:

1       2       3       4       5       25
6       7       8       9       10
Thanks for the replies, points taken.
I'm not quite understanding why this isn't working either.

1
2
3
4
5
6
    // -1 < k < NM is checked earlier 
    void method(int new_d, unsigned k)
    {
      vector<vector<int>> *A = &M;
      A->at(k).push_back(new_d);
    }
Last edited on
To be honest your use of pointers when dealing with a created vector can cause unintended problems.

Using single character variable names is another potential problem, variables should be descriptive and help self-document your code.
Both these scenarios do work as expected:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Trollop
{
  vector<vector<int>> M;

  public:
    Trollop::Trollop(unsigned NM)
    {  // Some NM
       M.reserve(NM);
    }

    void method1(int new_d, unsigned k)
    {
      vector<int> *A = &M[k];
      A->emplace_back(new_d);
    }

    void method2(int new_d, unsigned k)
    {
      vector<vector<int>> *A = &M;
      A->at(k).push_back(new_d);
    }
};


Now the only thing is, in the constructor when I call reserve() on M, I should instead be calling resize(). This is because M is a matrix and not a vector.
A pointer to the entire container itself, I see no harm in that.
A temporary pointer to a element contained within a container when the container is not being changed in any way, is sufficiently safe in my view.
This isn't production code - keeping the syntax compact is quite helpful in exposing the essence of the problem.
...
I'm sure we could still disagree on everything.
I'm not quite understanding why this isn't working either.

"isn't working" is not a helpful description of the problem. Is it failing to compile? Crashing when it runs? Exhibiting some other kind of unexpected behaviour.

Now the only thing is, in the constructor when I call reserve() on M, I should instead be calling resize(). This is because M is a matrix and not a vector.

I'm not sure what you mean by this. M is a vector. It behaves like any vector. The fact that its members are themselves vectors changes nothing about how you need to use M itself, regarding reserving memory, resizing it, and adding elements to it.
Last edited on
Did you see the first post for a description of "not working"?

M is a vector of vectors. A "vector" in the class lingo but also a matrix (2D-vector). I'm able to simply call reserve on 1D-vectors but if I do that on M then whilst the program compiles and runs, it fails to run properly and crashes in a seg. fault. In fact I have to call resize() on M for the method to work as expected.
Did you see the first post for a description of "not working"?

I did, but I have no way of knowing whether the problem with your updated code was the same as the one with your original code.

Being clear and precise about the nature of your problem is always a helpful thing.

M is a vector of vectors. A "vector" in the class lingo but also a matrix (2D-vector). I'm able to simply call reserve on 1D-vectors but if I do that on M then whilst the program compiles and runs, it fails to run properly and crashes in a seg. fault.

*Shrug* that's could be becase the problem is something to do with how you're using the "inner" vectors - i.e. the vectors which are elements of M. Or it could simply be because your code has undefined behaviour, which can lead to different behaviour under different circumstances.

But I can assure you, the fact that the elements of M are vectors changes nothing about how memory is managed in M. If a vector of vectors requires you to call M.resize(), then that means you should have done the same thing when using a vector of ints. The fact that it seemed to work when you didn't probably just means you have undefined behaviour, and you were unlucky in that it didn't cause a crash.

What you call a "Matrix" or a "2d vector" isn't something special that causes the std::vector class to behave differently from when elements are primitive types. It's just a normal, 1-D vector, that behaves like other normal, 1-D vectors. It's just the the elements themselves are also normal, 1-D vectors.
Last edited on
Now the only thing is, in the constructor when I call reserve() on M, I should instead be calling resize(). This is because M is a matrix and not a vector.


Do you understand the difference between reserve and resize? Reserve allocates memory for a specific capacity but doesn't do anything with it. Resize not only allocates the memory but actually creates a vector to reside in that memory.

This is why it appears to you that M being a "matrix" matters.

However, @MikeyBoy is right. The language doesn't care whether M is a vector<vector<int>> or a vector<int> or a vector<MyClass>. All C++ cares about is whether the vector has the element in it. So, in line 20, M[k] does not exist (when using reserve) because you never allocated the kth element of M. You never allocated any elements in M. When you use resize, all of the elements are allocated, so there's no problem indexing into the kth element.

All that being said, i agree that using pointers to a member variable is pretty confusing. I recommend rewriting the code as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Trollop
{
  vector<vector<int>> M;

  public:
    Trollop::Trollop(unsigned NM)
    {  // Some NM
       M.reserve(NM);
    }

    void method1(int new_d, unsigned k)
    {
      // first make sure that M has k elements
      M[k].emplace_back(new_d);
    }

    void method2(int new_d, unsigned k)
    {
      // first make sure that M has k elements
      M.at(k).push_back(new_d);
    }
};

	
You could have a matrix of numeric data or a matrix of any other abstract form including classes. It doesn't matter. I don't know why you'd assume it'd be one or the other.

Unlike <vector> where you don't really have restrictions on memory allocation in the singular direction, you can't assume this to be the case with matrices - namely you have to set the extent in at least one direction.
Unlike <vector> where you don't really have restrictions on memory allocation in the singular direction, you can't assume this to be the case with matrices - namely you have to set the extent in at least one direction.


No. That's an incorrect statement. Are you thinking of arrays in C?

First of all, there is no std::matrix in C++, so what you wrote is a "vector of vectors"--perfectly acceptable C++ code. Now, if you as the programmer decide that one or the other (or both) dimension of this construct must be fixed, that's perfectly fine, and an initial resize operation is totally appropriate. But it doesn't have to be that way.

There is nothing wrong with starting with an empty outer vector. As new rows (inner vectors) are added, the outer vector can be resized. Reserving room for the expected maximum outer vector size is a good idea, but if M[5] is the last row that is added, there is no need to create M[6] - M[11]. You can if you want to, but you don't need to.

You could also make the inner vectors (rows) a fixed size when they are created, or reserve space for each of them if you want to. That's a design/implementation decision based on the needs of the program.

If "you have to set the extent in at least one direction" is inherently part of the problem you are trying to solve, spell it out for us. But from a language perspective, it is not true.
For the sake of succinctness, I will call a 2d-vector a matrix. It can be a 2d-array of any given form or field. std:matrix doesn't exist so I don't know why you'd bring it into the discussion.

I assumed a matrix M of size N x P, where N was finite and P was possibly "unbounded". Unbounded has a stronger meaning but here it's only limited by stack/heap space.

The problem I faced was leaving N unbounded as you might have in the case of a vector. In this situation I was trying to access an nth row of M (-1 < n < N) in random order. Knowing the size of N in advance but not setting this size in advance led to the problem.
In this situation I was trying to access an nth row of M (-1 < n < N) in random order. Knowing the size of N in advance ...


Now that you have told us how you are using the matrix, I can see that this is a legitimate reason to size the outer vector at the beginning. In your situation, this appears to be a valid thing to do. I'm glad you resolved your problem.

However, in my previous post I reacted to your more general statement
you can't assume this to be the case with matrices - namely you have to set the extent in at least one direction
. I reacted because it is possible and frequently desirable to do just that. It sounded like you were saying that all 2d-vectors in all applications must be initialized with a fixed number of rows. But, many applications need to dynamically adjust the number of rows at run time with, for instance, emplace_back or resize. It all depends on the requirements and the problem being solved.

Good luck with the rest of your project.
In one of my other projects, where I was implementing the same sort of solutions, but in C, there was a dire need for linked-list type data structures. There usually is in almost any kind of program.

However by implementing linked lists using the conventional approach of dynamic memory allocation there resulted severe memory fragmentation. I then had to implement another model for a data structure that modelled something resembling linked list, and this was a preallocated block of lists where the block was recycled from top to bottom as it became full. This method didn't result in memory fragmentation but then it also meant you had to keep memory aside from the very outset.

In C++, all these problems are taken of. In fact C++ has streamlined development pace considerably. Particularly also due to its strictness on precision and correctness on implementation. In C I was spending a considerable amount of time just fixing bugs.
Topic archived. No new replies allowed.