1

I am attempting to initialize a matrix that is realized as a vector of vector of doubles. I.e.,

std::vector<std::vector<double>> A {std::vector<double> {}}; 
std::vector<std::vector<double>>::iterator it = A.begin();

My idea is to use a pointer to each "row", access the "push_back" through this pointer to fill up my rows. To make new rows, I simply use:

A.push_back(std::vector<double> {});

And then have my pointer point to the next row by simply:

it++;

And then fill my next row. The code complies just fine (I am using C++14 standard). However when I run it, I get this:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

Please consider the following code:

#include <iostream>
#include <vector>

void print(std::vector<std::vector<double>> matrix)
{
  typedef std::vector<std::vector<double>> row;
  typedef std::vector<double> col;
  for(row::iterator it = matrix.begin();it!=matrix.end();it++)
  {
    for(col::iterator ti = it->begin();ti!=it->end();ti++)
    {
      std::cout << *ti << ' ';
    }
    std::cout  << '\n';
  }
}

int main()
{
  std::vector<std::vector<double>> A {std::vector<double> {}};
  std::vector<std::vector<double>>::iterator it = A.begin();
  it->push_back(1);
  it->push_back(2);
  it->push_back(3);
  A.push_back(std::vector<double> {});
  it++;
  it->push_back(4);
  it->push_back(5);
  it->push_back(6);
  print(A);
  return 0;
}

(You may it for granted that the print function works as intended without errors, during compiling or runtime).

Any inputs will be helpful. Cheers

Nagesh Eranki
  • 125
  • 1
  • 9
  • 2
    Push-back invalidates iterators. You have to use vector::back().push_back(...) – kiloalphaindia May 09 '18 at 16:07
  • 2
    `std::vector>` is not the right type for a matrix anyway. Even if we ignore the performance implications, a matrix is a *rectangular* block of stuff and not a list of lists. The data structure should reflect that. – Baum mit Augen May 09 '18 at 16:27
  • @BaummitAugen I agree with you. However, I am novice C++ programmer with no background to data structures and algorithms. – Nagesh Eranki May 10 '18 at 10:12
  • Sure, that's why I'm telling you. This was not meant to be a personal attack. – Baum mit Augen May 10 '18 at 10:20

2 Answers2

2

Your vector<> iterators may be invalidated by the call to push_back().

A.push_back(std::vector<double> {});
//it may be invalid at this point because of new allocation in the vector
it++;

When you call push_back(), the vector will likely have to reallocate memory in order to accommodate the new data. This will cause the previous iterators to no longer point to a correct location in memory, and using them further will result in undefined behavior.

If you call A.begin() after the push_back() in order to reassign your iterator and then do the increment, then the code works as expected for me. Such as:

A.push_back(std::vector<double> {});
it = A.begin();
it++;

EDIT

If you would like to partially avoid the reassignment of the iterator to the beginning, you could use this:

it++;
it = A.insert(it,std::vector<double>{});

This will add a new vector<double> right before where it is pointing (in this case, just before the end of the current vector). This will then return an iterator stored in it that will point to the vector you just inserted, so you can use the result of the code as is.

Daniel
  • 1,291
  • 6
  • 15
  • Is there a way around having to reassign the iterator. You see, this is part of a bigger picture to create my 'matrix' class. I just tried using it = A.end() , without having to increment (right?). But now I have encountered a segfault – Nagesh Eranki May 09 '18 at 16:11
  • @NageshEranki Don't use `it = A.end()`. Use `it = A.back();`. – Hatted Rooster May 09 '18 at 16:30
  • @SombreroChicken Correct, however vector::back is not returning an iterator, but is the right way to do it. (see my answer) – kiloalphaindia May 09 '18 at 16:35
  • @NageshEranki I updated my answer to give a potential solution. kiloalphaindia provides another answer that should work in your specific case, but the iterator solution would generalize better to cases where you would like to insert/remove rows within the matrix instead of just the end – Daniel May 09 '18 at 17:22
0

As you want to append data to your outer vector you can also do:

std::vector<std::vector<double>> A {std::vector<double> {}};
A.back().push_back(1);
A.back().push_back(2);
A.back().push_back(3);
A.push_back(std::vector<double> {});
A.back().push_back(4);
A.back().push_back(5);
A.back().push_back(6);
print(A);

the author of the previous answer is of cause correct what concerns the invalidation of the iterator

For an implemantation of Matrix functions you may also use Eigen Template Libaray

kiloalphaindia
  • 561
  • 3
  • 7