2d vector modify with iterator

Viewed 50

I have a 2d matrix using vector library. And I wanted to iterate over the Matrix more conveniently, so I created an MatrixIterator class.

Matrix.cpp

#include <vector>

template <class T>
class MatrixIterator;

template <class T>
class Matrix
{
    friend class MatrixIterator<T>;

private:
public:
    std::vector<std::vector<T>> m;
    unsigned rows_;
    unsigned cols_;

    Matrix<T>(unsigned rows, unsigned cols);

    MatrixIterator<T> iterator() const
    {
        return {*this};
    }

    MatrixIterator<T> begin() const
    {
        return {*this};
    }

    MatrixIterator<T> end() const
    {
        return {*this, rows_, 0};
    }
}


template <class T>
class MatrixIterator
{
private:
    Matrix<T> matrix_;
    unsigned row_;
    unsigned col_;

public:
    MatrixIterator<T>(Matrix<T> m) : matrix_(m), row_(0), col_(0) {};
    MatrixIterator<T>(Matrix<T> m, unsigned row, unsigned col) : matrix_(m), row_(row), col_(col) {};

    MatrixIterator<T> begin() const
    {
        return {matrix_};
    }

    MatrixIterator<T> end() const
    {
        return {matrix_, matrix_.rows_, 0};
    }

    void inc()
    {
        if(++col_ >= matrix_.cols_)
        {
            row_++;
            col_ = 0;
        }
    }

    MatrixIterator<T>& operator++()
    {
        inc();
        return *this;
    }

    MatrixIterator<T> operator++(int)
    {
        inc();
        return *this;
    }

    bool operator!=(const MatrixIterator<T> &rhs) const
    {
        return (row_ != rhs.row_) || (col_ != rhs.col_);
    }

    T& operator*()
    {
        return matrix_.m[row_][col_];
    }
};


template <class T>
Matrix<T>::Matrix(unsigned rows, unsigned cols)
    : rows_(rows), cols_(cols)
{

    m.resize(cols);
    for (unsigned i = 0; i < cols; i++)
    {
        m[i].resize(rows);
        fill(m[i].begin(), m[i].end(), T());
    }
}

In the following code, when I try to manipulate value using iterator, it does not change the value. I tried returning the values as pointers from operator* but it did not work either. I saw no errors. What is wrong and how can I solve this?

main.cpp

#include "Matrix.cpp"
#include<iostream>
int main()
{
    Matrix<int> m = Matrix<int>{3,3};
    for(auto x: m.iterator())
        x = 10;
    for(auto x: m.iterator())
        std::cout << x << " ";
    // outputs 0 0 0 ~
}

Compiled with g++ main.cpp -std=c++20 -g -o main && main

2 Answers

You are iterating over values, not references when attempting to change the matrix values. Instead, try

for (auto& x : m.iterator())

You need to store a reference in the iterator class, other than hold a copy of it (iterator is just a view of the data).

template <class T>
class MatrixIterator {
 private:
  Matrix<T>& matrix_;
  unsigned row_;
  unsigned col_;

 public:
  MatrixIterator<T>(Matrix<T>& m) : MatrixIterator<T>(m, 0, 0) {}
  MatrixIterator<T>(Matrix<T>& m, unsigned row, unsigned col)
      : matrix_(m), row_(row), col_(col) {}
};

And you also need to non-const begin and end for your matrix, nonconst version iterator can be used to change the underlying value. The function iterator() can be removed here, since it's not common to have this in c++ code.

  MatrixIterator<T> begin() const { return {*this}; }
  MatrixIterator<T> begin() { return {*this}; }

  MatrixIterator<T> end() const { return {*this, rows_, 0}; }
  MatrixIterator<T> end() { return {*this, rows_, 0}; }

To change to the value with an iterator, you need a reference other than changing the copied value in your main function. It's not necessary to explicitly call the iterator here, the compiler will do it for you.

int main() {
  Matrix<int> m = Matrix<int>{3, 3};
  for (auto& x : m) x = 10;
  for (auto x : m) std::cout << x << " ";
  return 0;
}

Online demo.

Related