2

I am writing custom class Matrix using two-dimensional std::vector. So the problem is that I need to overload ++ operation for iterators so that I can run through the entire matrix at a time.

template <typename T>
class Matrix {
private:
    std::vector<std::vector<T>> arr;

public:
    Matrix(const std::vector<std::vector<T>>& tmp) {
        arr = tmp;
    }
    std::pair<size_t, size_t> size() const {
        std::pair<size_t, size_t> tmp;
        if (arr.empty()) {
            tmp.first = 0;
            tmp.second = 0;
        } else {
            tmp.first = arr.size();
            tmp.second = arr[0].size();
        }
        return tmp;
    }
    T operator () (size_t i, size_t j) const {
        return arr[i][j];
    }
    Matrix& transpose() {
        std::vector<std::vector<T>> tmp(size().second, std::vector<T>(size().first));
        for (size_t i = 0; i < size().first; ++i) {
            for (size_t j = 0; j < size().second; ++j) {
                tmp[j][i] = arr[i][j];
            }
        }
        *this = Matrix(tmp);
        return *this;
    }
    Matrix transposed() const {
        std::vector<std::vector<T>> tmp(size().second, std::vector<T>(size().first));
        for (size_t i = 0; i < size().first; ++i) {
            for (size_t j = 0; j < size().second; ++j) {
                tmp[j][i] = arr[i][j];
            }
        }
        return Matrix(tmp);
    }
    typename std::vector<T>::iterator begin() {
        return arr[0].begin();
    }
    typename std::vector<T>::iterator end() {
        return arr[size().first-1].end();
    }
};

For example, with the matrix mat = {{1,2},{3,4}}, this should work:

vector<vector<int>> arr = {{1,2},{3,4}};
Matrix mar(arr);
auto it = mat.begin();
while (it != mat.end()) {
    cout << *it << " ";
}

and the output should be:

1 2 3 4 

Can you please help me how to overload operator++(), begin() and end() for std::vector iterators?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
GoshkaLP
  • 137
  • 1
  • 11
  • 1
    It looks like you want to create your own iterator. This can be a little tricky if you've never done it before. Here's some [info about iterators](https://en.cppreference.com/w/cpp/iterator). – Indiana Kernick Mar 02 '20 at 10:30
  • Aside: If your homework allows it, consider `T operator[](std::pair) const;` (or `T&` and `const T&`) instead of `T operator () (size_t i, size_t j) const` – Caleth Mar 02 '20 at 11:08
  • Aside: `transpose()` can be simplified to `*this = transposed(); return *this;` – Caleth Mar 02 '20 at 11:10
  • Related: [Flattening Iterator](https://stackoverflow.com/q/3623082/2610810) – Caleth Mar 02 '20 at 11:14

3 Answers3

2

You will not be able to just take an iterator returned by std::vector and change any of its methods. If you want to, create your own iterator class (one that, for example, cointains two iterators into your internal vectors), that your matrix class will return, and implement the relevant methods for them.

DeducibleSteak
  • 1,398
  • 11
  • 23
2

I am writing custom class Matrix using two-dimensional std::vector.

Don't do that. Why?

  1. You're re-inventing the wheel. It is unlikely your use case is so special that no existing matrix / linear algebra library fits your needs.
  2. There are no two-dimensional std::vectors; you mean, a vector-of-vectors (which is what your example has). This will usually be somewhat slow, and besides - its semantics is probably not what you want (e.g. ability to resize individual rows, row storage scattered all over the heap etc.)

You might want to consider, for an example off the top of my head, the Eigen library's matrix class.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • I understand that I am invernting the wheel, but that's my home task :-( – GoshkaLP Mar 02 '20 at 10:50
  • *"This will usually be somewhat slow"*. I consider `std::vector>` an antipattern. There's almost never a good reason to use that over an `std::vector`. – Indiana Kernick Mar 02 '20 at 11:00
  • @IndianaKernick most common reason would be the very convenient ability to access the data like a grid via `data[x][y]`. I agree with you though. As a compromise, in a personal project I use a plain array to maintain the data and have a secondary array of pointers that point to the beginning of each row. Naturally this method has no automated boundary check but it is very performant. – AlexGeorg Mar 02 '20 at 12:04
  • @IndianaKernick: I agree. However: 1. I'm not sure OP knows what patterns and anti-patterns are. 2. I don't want to be harsh, so I chose to be vague. 3. If OP's work is mostly within single rows, then it's not that terrible. – einpoklum Mar 02 '20 at 12:16
  • @GoshkaLP: In that case, consider asking your teacher / teaching assistant whether this is what they really meant, because like others suggest - this would mean you need to implement const and non-const iterators for your class, which is a bit of a pain in the ass to do if you haven't done so before. – einpoklum Mar 02 '20 at 12:18
  • @einpoklum yeah... in the homework also there is a note that I need to implement const and non-const iterators so I can change the value... – GoshkaLP Mar 02 '20 at 14:21
1

So the decision using one-dimension(doesn't sound right) vector works. Thx everyone!

#include <iostream>
#include <utility>
#include <vector>

template <typename T>
class Matrix {
private:
    std::vector<T> arr;
    size_t m = 0;
    size_t n = 0;

public:
    Matrix(const std::vector<std::vector<T>>& tmp) {
        for (const auto& row : tmp) {
            for (const auto& x : row) {
                arr.push_back(x);
            }
        }
        if (!tmp.empty()) {
            m = tmp.size();
            n = tmp[0].size();
        }
    }
    std::pair<size_t, size_t> size() const {
        std::pair<size_t, size_t> tmp;
        tmp.first = m;
        tmp.second = n;
        return tmp;
    }
    T operator () (size_t i, size_t j) const {
        return arr[i * m + j];
    }
    Matrix transposed() const {
        std::vector<std::vector<T>> tmp(size().second, std::vector<T>(size().first));
        for (size_t i = 0; i < size().first; ++i) {
            for (size_t j = 0; j < size().second; ++j) {
                tmp[j][i] = arr[i * m + j];
            }
        }
        return Matrix(tmp);
    }
    Matrix& transpose() {
        *this = transposed();
        return *this;
    }
    auto begin() {
        return arr.begin();
    }
    auto end() {
        return arr.end();
    }
    auto begin() const {
        return arr.begin();
    }
    auto end() const {
        return arr.end();
    }
};
template <typename T>
std::ostream& operator<<(std::ostream& out, const Matrix<T>& m) {
    for (size_t i = 0; i < m.size().first; ++i) {
        for (size_t j = 0; j < m.size().second; ++j) {
            if (j == 0)
                out << m(i, j);
            else
                out << "\t" << m(i, j);
        }
        if (i != m.size().first - 1)
            out << "\n";
    }
    return out;
}

template <typename T>
Matrix<T> operator + (const Matrix<T>& m1, const Matrix<T>& m2) {
    std::vector<std::vector<T>> tmp(m1.size().first, std::vector<T>(m1.size().second));
    for (size_t i = 0; i < m1.size().first; ++i) {
        for (size_t j = 0; j < m1.size().second; ++j) {
            tmp[i][j] = m1(i, j) + m2(i, j);
        }
    }
    return Matrix(tmp);
}

template <typename T>
Matrix<T>& operator += (Matrix<T>& m1, const Matrix<T>& m2) {
    m1 = m1 + m2;
    return m1;
}

template <typename T1, typename T2>
Matrix<T1> operator * (const Matrix<T1>& m, const T2& x) {
    std::vector<std::vector<T1>> tmp(m.size().first, std::vector<T1>(m.size().second));
    for (size_t i = 0; i < m.size().first; ++i) {
        for (size_t j = 0; j < m.size().second; ++j) {
            tmp[i][j] = m(i, j) * x;
        }
    }
    return Matrix(tmp);
}

template <typename T1, typename T2>
Matrix<T1> operator * (const T2& x, const Matrix<T1>& m) {
    return m * x;
}

template <typename T1, typename T2>
Matrix<T1>& operator *= (Matrix<T1>& m, const T2& x) {
    m = m * x;
    return m;
}

template <typename T>
Matrix<T> operator * (const Matrix<T>& first, const Matrix<T>& second) {
    std::vector<std::vector<T>> tmp(first.size().first, std::vector<T>(second.size().second));
    T x = 0;
    for (size_t i = 0; i < first.size().first; ++i) {
        for (size_t j = 0; j < second.size().second; ++j) {
            for (size_t k = 0; k < first.size().second; ++k) {
                x += first(i, k) * second(k, j);
            }
            tmp[i][j] = x;
            x = 0;
        }
    }
    return Matrix(tmp);
}

template <typename T>
Matrix<T>& operator *= (Matrix<T>& first, const Matrix<T>& second) {
    first = first * second;
    return first;
}

GoshkaLP
  • 137
  • 1
  • 11