c++ how to correctly overload + operator - c++

I need to implement a matrix class for my study, and now I'm stuck with operator + overloading.
Here's my matrix.cpp code:
template<class type>
matrix<type> matrix<type>::operator+ (const matrix<type>& matObj){
matrix<type> newMat(this->WIDTH, this->HEIGHT, 0);
for (int i = 0; i < this->HEIGHT; i++)
for (int j = 0; j < this->WIDTH; j++)
newMat.array[WIDTH * i + j] = matObj.array[WIDTH * i + j] + this->array[WIDTH * i + j];
return newMat;
}
And here's main.cpp code:
int main() {
vector< vector<int>> v = {
{1, 2, 3},
{4, 5, 6},
{1, 2, 9}
};
math::matrix<int> mat1(v), mat2;
mat2 = mat1+mat1;
for (int i = 0; i < mat2.cols(); i++) {
for (int j = 0; j < mat2.rows(); j++) {
cout << mat2[i][j] << ", ";
}
cout << endl;
}
cin.get();
return 0;
}
Assume that all constructors and other codes are written correctly.
So the problem is when I try to run the program, it fails on line:
mat2 = mat1+mat1;
And when I try to print newMat in matrix.cpp before the return statement, it prints it correctly. And of course, I have implemented = operator which works fine. Any suggestions?

The easy way to implement + is as follows.
struct example {
int state = 0;
example& operator+=( example const& rhs )& {
// increment state here; change in real code
state += rhs.state;
return *this;
}
friend example operator+( example lhs, example const& rhs ) {
lhs += rhs;
return std::move(lhs);
}
example( example&& ) = default; // ensure this is efficient
example& operator=( example&& ) = default; // ensure this is efficient
example& operator=( example const& ) = default; // this can be expensive
example( example const& ) = default; // this can be expensive
};
more concretely
template<class T>
struct matrix {
std::vector< std::vector<T> > state;
matrix & operator+=( matrix const& rhs )& {
// increment state here; change in real code
for (std::size_t i = 0; i < (std::min)(state.size(), rhs.state.size(); ++i) {
for (std::size_t j = 0; j < (std::min)(state[i].size(), rhs.state[i].size(); ++j) {
state[i][j] += rhs.state[i][j];
}
}
return *this;
}
friend matrix operator+( matrix lhs, matrix const& rhs ) {
lhs += rhs;
return std::move(lhs);
}
matrix( matrix&& ) = default; // ensure this is efficient
matrix& operator=( matrix&& ) = default; // ensure this is efficient
matrix& operator=( matrix const& ) = default; // this can be expensive
matrix( matrix const& ) = default; // this can be expensive
}
=default works on matrix(matrix&&) etc because I followed the rule of 0 -- I left data storage to a specialized class (std::vector<?>), and it has efficient move semantics, so I get it automatically.
If you swap state for a std::vector< T > that has width*height elements, you just have to adapt how you access it. (That is more efficient, but I was trying to keep it simple).

Related

Is there a way to extract outer loops of multiple similar functions?

Example: I want to extract the nested for loops from these operator functions that are the same except the one line.
// Add two matrices
Matrix& operator+=(const Matrix& other)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = (*this)(i, j) + other(i, j); // Only difference
}
}
return *this;
}
// Subtract two matrices
Matrix& operator-=(const Matrix& other)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = (*this)(i, j) - other(i, j); // Only different
}
}
return *this;
}
You can write a function template that accepts a binary function and applies it on all pairs of elements inside the loops
template<typename Op>
void loops(const Matrix& other, Op op)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = op((*this)(i, j), other(i, j));
}
}
}
and then use it like this
// Add two matrices
Matrix& operator+=(const Matrix& other)
{
loops(other, std::plus{});
return *this;
}
// Subtract two matrices
Matrix& operator-=(const Matrix& other)
{
loops(other, std::minus{});
return *this;
}
I'd argue that this sort of problem often implies a poor abstraction.
In the example case, an efficient matrix will have a single contiguous array, where matrix(i,j) is translated to array[i*n_columns+j] behind the scenes. The i,j interface is simpler in many cases (otherwise you'd just use a vector), but there's no reason to restrict the user accessing the underlying array elements directly - let alone within your own matrix class!
Another way to put it is that you're using classes that create the (i,j) abstraction for you, and now you're wanting another layer of abstraction to undo the work. This is costly in cpu time and in your time, and makes for spaghetti code. Instead, make sure that you (and your user) has access to the underlying array by direct element access and by iterator:
public:
auto & operator [] (size_t i)
{
return data[i]; // our underlying array
}
const auto & operator [] (size_t i) const
{
return data[i];
}
Matrix& operator += (const Matrix& other)
{
for (size_t i = 0; i < size(); ++i)
data[i] += other[i];
return *this;
}
Matrix& operator -= (const Matrix& other)
{
for (size_t i = 0; i < size(); ++i)
data[i] -= other[i];
return *this;
}
You may think that this somehow breaks encapsulation, but it doesn't. The user has the same access to edit elements, but no access to change the matrix size or access to the raw pointer. However, for the sake of answering your more general question about avoiding loops, I'll pretend we only have access to the iterators:
Matrix& operator += (const Matrix& other)
{
auto it1 = begin();
auto it2 = other.begin();
for (size_t i = 0; i < size(); ++i)
it1[i] += it2[i];
return *this;
}
Matrix& operator -= (const Matrix& other)
{
auto it1 = begin();
auto it2 = other.begin();
for (size_t i = 0; i < size(); ++i)
it1[i] -= it2[i];
return *this;
}
Okay, that's better, but we can abstract the repetitive part for any iterator or container, like so:
template <class Func, class ... Its>
void ForArrays (size_t size, Func&& f, Its && ... its)
{
for (size_t i = 0; i < size; ++i)
f(its[i]...);
}
template <class Func, class ... Cs>
void ForContainers (Func&& f, Cs && ... cs)
{
size_t size = (cs.size(), ...);
assert(((size == cs.size()) && ...));
ForArrays(size, f, cs.begin()...);
}
Demo
And now we can rewrite our operators:
Matrix& operator += (const Matrix& other)
{
ForContainers([](auto& lhs, auto rhs){lhs += rhs}, *this, other);
return *this;
}
Matrix& operator -= (const Matrix& other)
{
ForContainers([](auto& lhs, auto rhs){lhs -= rhs}, *this, other);
return *this;
}
I think the lesson here is to never undo the work of an abstraction; instead, if it's not useful for part of your code then avoid it entirely (in that part of the code). A layer of abstraction to undo a layer of abstraction is why some people turn away from OOP.

C++ operator overloading fails to output + operation

I'm learning C++ and trying to write a C++ class for matrices, where I store the matrix as a one-dimensional C array. To this end, I defined an element member function to access the matrix elements based on their location in the array. I have then overloaded the << and + operators to handle the display and addition of matrices. The << operator works as intended, as can be demonstrated in the following example:
#include<iostream>
class matrix {
friend std::ostream & operator<<(std::ostream &os, matrix &M);
private:
int rows{}, columns{};
double *array {nullptr};
public:
matrix(int rows, int columns);
~matrix() {}
double & element(int r, int c);
matrix operator+(matrix &M)
{
matrix N(rows, columns);
if (M.rows!=rows || M.columns!=columns){
std::cout << "ERROR: DIMENSIONS OF MATRICES DO NOT MATCH" << std::endl;
}
else{
for (int i{1}; i<=M.rows; i++)
for (int j{1}; j<=M.columns; j++){
N.element(i,j) = element(i,j) + M.element(i,j);
}
}
return N;
}
};
matrix::matrix(int r, int c)
{
rows = r;
columns = c;
array = new double[rows*columns];
for(int i{0}; i<rows*columns; i++) array[i]=0.0;
}
double & matrix::element(int i, int j)
{
int loc{0};
loc = (j-1) + (i-1)*columns;
return array[loc];
}
std::ostream & operator<<(std::ostream &os, matrix &M)
{
for (int i{1}; i<=M.rows; i++){
os << "[ ";
for (int j{1}; j<=M.columns; j++){
os << M.element(i,j) << " ";
}
os << "] \n";
}
return os;
}
int main() {
matrix A(2,2);
matrix B(2,2);
A.element(1,1) = 1;
A.element(1,2) = 2;
A.element(2,1) = 3;
A.element(2,2) = 4;
B.element(1,1) = 1;
B.element(1,2) = 2;
B.element(2,1) = 3;
B.element(2,2) = 4;
std::cout << A << std::endl;
std::cout << B << std::endl;
return 0;
}
I'm then unable to display the addition of two matrices:
std::cout << A+B << std::endl;
If I, however, break up the operation and add the matrices separately before displaying their sum, I get the right output, which makes me believe that the + operator also works correctly:
matrix C(2,2);
C = A+B;
std::cout << C << std::endl;
The error seems to suggest that there may be an issue converting the matrix elements into the ostream, but it's curious that the above workaround solution works.
Your << operator takes a non-const reference to a matrix as parameter. That means it can't take reference to a temporary object.
The result of A+B is a temporary object if you don't assign it to something.
So you need to change this:
std::ostream & operator<<(std::ostream &os, matrix &M);
into this:
std::ostream & operator<<(std::ostream &os, const matrix &M);
You may sooner or later encounter the same problem with your +-operator:
matrix operator+(matrix &M)
should be
// Both `M` and `*this` should be const
matrix operator+(const matrix &M) const
Naturally you will then have a problem with your element method which can only act on a non-const object, so you also need a variant that acts on a const object:
class matrix
{
....
public:
double & element(int r, int c);
double element(int r, int c) const;
...
}
You can't take a non-const reference of a temporary object. You're trying to call operator<< with a temporary (A+B) but the signature of your overloaded operator<< takes a non-const reference so your function isn't a valid candidate.
To fix this you'll need to change a couple things. First, make your overloaded << operator take a const reference to the matrix. And don't forget to fix the friend signature in the class declaration.
std::ostream & operator<<(std::ostream &os, const matrix &M);
Just doing that will still cause errors because the element method is only defined for non-const objects. You'll need to add an overload for element that acts on const matrixes (and that won't let you modify the returned element).
double matrix::element(int i, int j) const
{
int loc = (j-1) + (i-1)*columns;
return array[loc];
}
Your operator<< and operator+ both need to take the input matrix by const reference, so that they can accept temporary matrix objects as input.
Also, operator+ should be const-qualified since it doesn't modify the contents of this. And element() should have a const -qualified overload so it can provide read-only access to a const matrix object.
You are also missing a copy constructor, a copy-assignment operator, a move constructor, and a move-assignment operator, per the Rule of 3/5/0. And also, your destructor is not freeing the C array at all, so it is being leaked.
Try this instead:
#include <iostream>
#include <stdexcept>
#include <utility>
class matrix {
friend std::ostream& operator<<(std::ostream &os, const matrix &M);
private:
int rows{}, columns{};
double* array{nullptr};
public:
matrix(int rows, int columns);
matrix(const matrix &M);
matrix(matrix &&M);
~matrix();
matrix& operator=(matrix M);
double& element(int r, int c);
double element(int r, int c) const;
matrix operator+(const matrix &M) const;
matrix& operator+=(const matrix &M);
};
matrix::matrix(int r, int c)
: rows(r), columns(c)
{
size_t size = rows*columns;
array = new double[size];
for(size_t i = 0; i < size; ++i)
array[i] = 0.0;
}
matrix::matrix(const matrix &M)
: rows(M.rows), columns(M.columns)
{
size_t size = rows*columns;
array = new double[size];
for(size_t i = 0; i < size; ++i)
array[i] = M.array[i];
}
matrix::matrix(matrix &&M)
: rows(M.rows), columns(M.columns), array(M.array)
{
M.rows = M.columns = 0;
M.array = nullptr;
}
matrix::~matrix()
{
delete[] array;
}
matrix& operator=(matrix M)
{
std::swap(rows, M.rows);
std::swap(columns, M.columns);
std::swap(array, M.array);
return *this;
}
double& matrix::element(int r, int c)
{
// TODO: do bounds checking here...
size_t loc = ((r-1)*columns) + (c-1);
return array[loc];
}
double matrix::element(int r, int c) const
{
// TODO: do bounds checking here...
size_t loc = ((r-1)*columns) + (c-1);
return array[loc];
}
std::ostream & operator<<(std::ostream &os, const matrix &M)
{
for (int r = 1; r <= M.rows; ++r){
os << "[ ";
for (int c = 1; c <= M.columns; ++c){
os << M.element(r,c) << " ";
}
os << "]\n";
}
/* alternatively...
size_t loc = 0;
for (int r = 0; r < M.rows; ++r){
os << "[ ";
for (int c = 0; c < M.columns; ++c){
os << M.array[loc++] << " ";
}
os << "]\n";
}
*/
return os;
}
matrix matrix::operator+(const matrix &M) const
{
matrix N(*this);
N += M;
return N;
}
matrix& matrix::operator+=(const matrix &M)
{
if (M.rows != rows || M.columns != columns){
throw std::runtime_error("DIMENSIONS OF MATRICES DO NOT MATCH");
}
for (int r = 1; r <= rows; ++r)
for (int c = 1; c <= columns; ++c){
element(r,c) += M.element(r,c);
}
}
/* alternatively:
size_t size = rows*columns;
for (size_t i = 0; i < size; ++i)
array[i] += M.array[i];
}
*/
return *this;
}
int main() {
matrix A(2,2);
matrix B(2,2);
A.element(1,1) = 1;
A.element(1,2) = 2;
A.element(2,1) = 3;
A.element(2,2) = 4;
B.element(1,1) = 1;
B.element(1,2) = 2;
B.element(2,1) = 3;
B.element(2,2) = 4;
std::cout << A << std::endl;
std::cout << B << std::endl;
return 0;
}

How to construct a convenient iterator over blocks of Matrix using Eigen

I want to construct a "block-columnwise" iterator which runs over all possible blocks of of Dense Matrix with Eigen.
#include <iostream>
#include <Eigen/Dense>
int main() {
Eigen::MatrixXd A(4,4);
A << 1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16;
for (Eigen::Index k=0; k< ? ; ++k) {
//?????
Eigen::Iterator it.... ;
std::cout<<it<<std::endl;
}
}
The output should be:
1 2
5 6
9 10
13 14
3 4
7 8
11 12
15 16
Essentially, I'm searching a nice version of
#include <iostream>
#include <Eigen/Dense>
int main() {
Eigen::MatrixXd A(4,4);
A << 1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16;
int numberOfBlocksPerDir =2;
for (int i = 0; i < numberOfBlocksPerDir ; i++) {
for (int j = 0; j < numberOfBlocksPerDir ; j++) {
Eigen::Block<Eigen::MatrixXd, 2, 2> currentBlock = A.block<2, 2>(numberOfBlocksPerDir * j, numberOfBlocksPerDir * i);
std::cout<<currentBlock <<std::endl;
}
}
}
Is something like that possible using Eigen build-in functions? If not what would be a nice way to accomplish this on my own? ( Defining my own iterator?)
I couldn't find anything in the Eigen documentation, but a simple forward iterator doesn't need too much:
equality operators
increment operators
dereference operator
This is what I came up with
template <Eigen::Index RowSize, Eigen::Index ColSize>
struct EigenBlockIt {
using Self = EigenBlockIt<RowSize, ColSize>;
// constructors for end()/begin() equivalents
EigenBlockIt() : matrix(nullptr) , col(0), row(0) { }
EigenBlockIt(const Eigen::MatrixXd& matrix) : matrix(&matrix) , col(0), row(0) { }
// comparison
friend bool operator ==(const Self& lhs, const Self& rhs) {
return lhs.matrix == rhs.matrix && lhs.row == rhs.row && lhs.col == rhs.col;
}
friend bool operator !=(const Self& lhs, const Self& rhs) {
return lhs.matrix != rhs.matrix || lhs.row != rhs.row || lhs.col != rhs.col;
}
// increment (postfix/prefix)
Self& operator++() {
row++;
if (row >= matrix->rows() / RowSize) {
row = 0;
col++;
if (col >= matrix->cols() / ColSize) {
matrix = nullptr;
row = 0;
col = 0;
}
}
return *this;
}
Self operator++(int) {
EigenBlockIt it = *this;
++(*this);
return it;
}
// dereference
const Eigen::Block<const Eigen::MatrixXd, RowSize, ColSize> operator *() const {
return matrix->block<RowSize, ColSize>((matrix->rows() / RowSize) * row, (matrix->cols() / ColSize) * col);
}
const Eigen::MatrixXd* matrix;
Eigen::Index row;
Eigen::Index col;
};
Usage:
for (auto it = EigenBlockIt<2, 2>(A); it != EigenBlockIt<2, 2>(); ++it) {
std::cout << *it << std::endl;
}
Demo: https://godbolt.org/z/boz9xG

How to overload + to sum 2 objects that are matrix in c++?

i have this class
class Matrix
{
int size;
std::unique_ptr<std::unique_ptr<int[]>[]> val;
public:
Matrix(int size1)
{
size=size1;
val=std::make_unique< std::unique_ptr<int[]>[] >(size);
...
}
... move constructor,move assignment operator
Matrix& operator+(Matrix &m)
{
Matrix sumMatrix(size);
for ( int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j){
sumMatrix.val[i][j]=this->val[i][j]+m.val[i][j];
}
}
return sumMatrix;
}
and main :
...
Matrix e=b+c;
std::cout<<"e="<<std::endl;
e.print();
and i have this error :
warning: reference to local variable 'sumMatrix' returned
[-Wreturn-local-addr]
Matrix sumMatrix(size);
Can someone please help me with this ??
Return by value, as you should for operator+ most of the time:
// vvv--Removed & vvvvv-----vvvvv--Const is more appropriate here
Matrix operator+(Matrix const &m) const { ... }
This will require a copy constructor, make sure to add that. Also note that you should probably collect your for-loop logic into an operator+= and simplify operator+ significantly while providing more functionality for the end-user:
Matrix& operator+=(Matrix const& m) {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
//vvv--No need for this-> in C++
val[i][j] += m.val[i][j];
}
}
return *this;
}
Matrix operator+(Matrix const& m) const {
Matrix sumMatrix{m}; // requires a copy constructor.
sumMatrix += *this;
return sumMatrix;
}

Checking the matrix size

I don't really know how to check the size of both matrices, if they are appropriate to be added.
Here's my code, which is currently running without errors:
matrix operator+(const matrix & mat){
matrix add;
add.mSize = mat.mSize;
add.mP = new int[add.mSize * add.mSize];
for(int i = 0; i < add.mSize * add.mSize; i++){
add.mP[i] = mP[i] + mat.mP[i];
}
return add;
}
A general 2d matrix has two different dimensions, rows and columns (unless it is the particular case of a square matrix).
In the general case, your code could be:
#include <stdexcept>
class matrix
{
public:
matrix(int rows, int columns) :
mRows(rows), mColumns(columns), mP(NULL)
{
mP = new double[mRows * mColumns];
for (int i = 0; i < mRows * mColumns; i++)
mP[i] = 0.0;
}
matrix(const matrix& other) :
mRows(other.mRows), mColumns(other.mColumns), mP(NULL)
{
mP = new double[mRows * mColumns];
for (int i = 0; i < mRows * mColumns; i++)
mP[i] = other.mP[i];
}
~matrix()
{
delete[] mP;
}
const double* operator[] (int row) const {
return &mP[row * mColumns];
}
double* operator[] (int row) {
return &mP[row * mColumns];
}
matrix& operator=(const matrix& other) {
if (this == &other) return *this;
if ((mRows != other.mRows) || (mColumns != other.mColumns)) throw std::invalid_argument( "dimensions don't match" );
for (int r = 0; r < mRows; r++) {
for (int c = 0; c < mColumns; c++) {
(*this)[r][c] = other[r][c];
}
}
return *this;
}
matrix& operator+=(const matrix& other) {
if ((mRows != other.mRows) || (mColumns != other.mColumns)) throw std::invalid_argument( "dimensions don't match" );
for (int r = 0; r < mRows; r++) {
for (int c = 0; c < mColumns; c++) {
(*this)[r][c] += other[r][c];
}
}
return *this;
}
private:
int mRows;
int mColumns;
double *mP;
};
inline matrix operator+(matrix lhs, const matrix& rhs)
{
lhs += rhs;
return lhs;
}