template argument deduction for a allocating Matrix - c++

I have this Matrix class that allocates the data on the heap and a helper class M that is a Matrix with the data as C-style array, no allocation. Template argument deduction works for the helper class automatically. But not for the Matrix class.
I can construct a Matrix from that with template argument deduction:
M m{{{1, 2}, {3, 4}}};
Matrix a{M{{{1, 2}, {3, 4}}}};
What I'm looking for is to get rid of the helper class so the following works:
Matrix a{{{1, 2}, {3, 4}}};
Here is a working example with the helper class: https://godbolt.org/z/46vEqbvax
#include <cstddef>
#include <type_traits>
#include <cstring>
#include <initializer_list>
#include <utility>
#include <memory>
#include <cassert>
#include <algorithm>
template <typename T, std::size_t rows, std::size_t cols>
class M {
public:
const T * operator[](std::size_t x) const {
return data[x];
}
T * operator[](std::size_t x) {
return data[x];
}
T data[rows][cols]{};
};
template <typename T, std::size_t rows, std::size_t cols>
class Matrix {
private:
T *data{new T[rows * cols]};
public:
Matrix() { }
~Matrix() {
delete[] data;
data = nullptr; // crash on use after free
}
Matrix(const Matrix &other) {
*this = other;
}
Matrix(T &&other) : data(other.data) {
other.data = nullptr;
}
Matrix & operator=(const Matrix &other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j) {
(*this)[i][j] = other[i][j];
}
}
}
return *this;
}
Matrix operator=(Matrix &&other) {
swap(data, other.data);
return *this;
}
const T * operator[](std::size_t x) const {
return &data[x * cols];
}
T * operator[](std::size_t x) {
return &data[x * cols];
}
Matrix(const M<T, rows, cols>& other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j) {
(*this)[i][j] = other[i][j];
}
}
}
}
Matrix(M<T, rows, cols>&& other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j) {
std::swap((*this)[i][j], other[i][j]);
}
}
}
}
};
//template <typename T, std::size_t rows, std::size_t cols>
//Matrix(M<T, rows, cols>) -> Matrix<T, rows, cols>;
#include <iostream>
int main() {
Matrix a{M{{{1, 2}, {3, 4}}}};
Matrix b{a};
std::cout << b[0][0] << " " << b[0][1] << std::endl;
std::cout << b[1][0] << " " << b[1][1] << std::endl;
}

You can get rid of helper class M and have your Matrix template parameters automatically deduced by changing the type of your constructor parameters to (l- and r-value references to) C-style arrays:
//Matrix(const M<T, rows, cols>& other) {
Matrix(const T (&other)[rows][cols]) {
// ... same implementation ...
}
//Matrix(M<T, rows, cols>&& other) {
Matrix(T (&&other)[rows][cols]) {
// ... same implementation ...
}
Demo

Related

Can one make a 2D matrix class where often accessed sizes, rows and cols, are const members and not getter functions?

I'm continually irritated that matrix dimensions are accessed with getters. Sure one can have public rows and cols but these should be const so that users of the Matrix class can't directly modify them. This is a natural way of coding but, because using const members is regarded, apparently incorrectly, as not possible without UB, results in people using non-const fields when they really should be consts that are mutable when needed such as resizing, or assigning.
What I'd like is something that can be used like this:
Matrix2D<double> a(2, 3);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
but where a.rows=5; isn't compilable because it's const. And it would be great if the class can be included in vectors and other containers.
Now comes: Implement C++20's P0784 (More constexpr containers)
https://reviews.llvm.org/D68364?id=222943
It should be doable without casts or UB and can even be done when evaluating const expressions. I believe c++20 has made it doable by providing the functions std::destroy_at and std::construct_at. I have attached an answer using c++20 that appears to show it is indeed possible, and easily done, to provide const member objects in classes without an undue burden.
So the question is do the new constexpr functions in c++20, which provide the ability to destroy and then construct an object with different consts valid? It certainly appears so and it passes the consexpr lack of UB test.
If you want to have const properties you can try this :
class Matrix {
public:
const int rows;
const int cols;
Matrix(int _rows, int _cols) : rows(_rows), cols(_cols)
{
}
};
The rows and cols properties of a Matrix instance are initialized once in the constructor.
Well, here's my pass at using const rows and cols in a 2D matrix. It passes UB tests and can be used in collections like vector w/o weird restrictions. It's a partial implementation. No bounds checking and such but to provide a possible approach to the problem.
matrix2d.h
#pragma once
#include <memory>
#include <vector>
#include <initializer_list>
#include <stdexcept>
#include <array>
template<class T>
class Matrix2D
{
std::vector<T> v;
public:
const size_t rows;
const size_t cols;
constexpr Matrix2D(const Matrix2D& m) = default;
constexpr Matrix2D(Matrix2D&& m) noexcept = default;
explicit constexpr Matrix2D(size_t a_rows=0, size_t a_cols=0) : v(a_rows* a_cols), rows(a_rows), cols(a_cols) {}
constexpr Matrix2D(std::initializer_list<std::initializer_list<T>> list) : rows(list.size()), cols((list.begin())->size())
{
for (auto& p1 : list)
for (auto p2 : p1)
v.push_back(p2);
}
constexpr Matrix2D& operator=(const Matrix2D& mat)
{
std::vector<T> tmp_v = mat.v;
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(tmp_v);
return *this;
}
constexpr Matrix2D& operator=(Matrix2D&& mat) noexcept
{
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(mat.v);
return *this;
}
// user methods
constexpr T* operator[](size_t row) { // alternate bracket indexing
return &v[row * cols];
};
constexpr T& operator()(size_t row, size_t col)
{
return v[row * cols + col];
}
constexpr const T& operator()(size_t row, size_t col) const
{
return v[row * cols + col];
}
constexpr Matrix2D operator+(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] += v1.v[i];
return ret;
}
constexpr Matrix2D operator-(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] -= v1.v[i];
return ret;
}
constexpr Matrix2D operator*(Matrix2D const& v1) const
{
if (cols != v1.rows) throw std::range_error("cols of first must == rows of second");
Matrix2D v2 = v1.transpose();
Matrix2D ret(rows, v1.cols);
for (size_t row = 0; row < rows; row++)
for (size_t col = 0; col < v1.cols; col++)
{
T tmp{};
for (size_t row_col = 0; row_col < cols; row_col++)
tmp += this->operator()(row, row_col) * v2(col, row_col);
ret(row, col) = tmp;
}
return ret;
}
constexpr Matrix2D transpose() const
{
Matrix2D ret(cols, rows);
for (size_t r = 0; r < rows; r++)
for (size_t c = 0; c < cols; c++)
ret(c, r) = this->operator()(r, c);
return ret;
}
// Adds rows to end
constexpr Matrix2D add_rows(const Matrix2D& new_rows)
{
if (cols != new_rows.cols) throw std::range_error("cols cols must match");
Matrix2D ret = *this;
std::construct_at(&ret.rows, rows + new_rows.rows);
ret.v.insert(ret.v.end(), new_rows.v.begin(), new_rows.v.end());
return ret;
}
constexpr Matrix2D add_cols(const Matrix2D& new_cols)
{
if (rows != new_cols.rows) throw std::range_error("rows must match");
Matrix2D ret(rows, cols + new_cols.cols);
for (size_t row = 0; row < rows; row++)
{
for (size_t col = 0; col < cols; col++)
ret(row, col) = this->operator()(row, col);
for (size_t col = cols; col < ret.cols; col++)
ret(row, col) = new_cols(row, col - cols);
}
return ret;
}
constexpr bool operator==(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols || v.size() != v1.v.size())
return false;
for (size_t i = 0; i < v.size(); i++)
if (v[i] != v1.v[i])
return false;
return true;
}
constexpr bool operator!=(Matrix2D const& v1) const
{
return !(*this == v1);
}
// friends
template <size_t a_rows, size_t a_cols>
friend constexpr auto make_std_array(const Matrix2D& v)
{
if (a_rows != v.rows || a_cols != v.cols) throw std::range_error("template cols and rows must be the same");
std::array<std::array<T, a_cols>, a_rows> ret{};
for (size_t r = 0; r < a_rows; r++)
for (size_t c = 0; c < a_cols; c++)
ret[r][c] = v(r, c);
return ret;
}
};
Source.cpp
#include <vector>
#include <algorithm>
#include <iostream>
#include "matrix2d.h"
constexpr std::array<std::array<int, 3>, 4> foo()
{
Matrix2D<double> a(2, 3), b;
Matrix2D d(a);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
b = a + a;
Matrix2D<int> z1{ {1},{3},{5} };
z1 = z1.add_cols({{2}, {4}, {6}});
z1 = z1.add_rows({{7,8}});
// z1 is now {{1,2},{3,4},{5,6},{7,8}}
Matrix2D<int> z2{ {1,2,3},{4,5,6} };
Matrix2D<int> z3 = z1 * z2; // z3: 4 rows, 3 cols
// from separate math program product of z1 and z2
Matrix2D<int> ref{ {9,12,15},{19,26,33},{29,40,51},{39,54,69} };
// test transpose
Matrix2D<int> tmp = ref.transpose(); // tmp: now 3 rows, 4 cols
if (ref == tmp) throw; // verify not equal
tmp = tmp.transpose(); // now ref==tmp==z3
if (ref != tmp || ref != z3) throw;
// Check using vector of matrixes, verify sort on row size works
std::vector<Matrix2D<int>> v;
v.push_back(z1);
v.push_back(z2);
v.push_back(z1);
v.push_back(z2);
std::sort(v.begin(), v.end(), [](auto const& m1, auto const& m2) {return m1.rows < m2.rows; });
for (size_t i = 0; i < v.size() - 1; i++)
if (v[i].rows > v[i + 1].rows) throw;
// return constexpr of 2D std::array
std::array<std::array<int, 3>, 4> array = make_std_array<4,3>(ref); // ref must have 4 rows, 3 cols
return array;
}
int main()
{
auto print = [](auto& a) {
for (auto& x : a)
{
for (auto y : x)
std::cout << y << " ";
std::cout << '\n';
}
std::cout << '\n';
};
// run time
auto zz = foo();
print(zz);
// validating no UB in Matrix2D;
// and creating a nested, std::array initialized at compile time
//constexpr std::array<std::array<int, 3>, 4> x = foo();
constexpr std::array<std::array<int, 3>, 4> x = foo(); // compile time
print(x);
return 0;
}

Returning template class object

I have a template of a matrix class:
template <typename T>
class Matrix {
And many functions (like adding two matrices) return std::optional. I wanted to make an operator that would unwrap the value (or throw an exception):
template <class T>
Matrix<T> operator!(const std::optional<Matrix<T>>& other) {
return other.value();
}
Doing that I get the error C2440: "Cannot convert from const _Ty to Matrix< int > with [ _Ty = Matrix]". It says that construction of Matrix class cannot be done because of ambiguous copying constructors or unavailable copying constructor.
EDIT:
#include <iostream>
#include <optional>
template <typename T>
class Matrix {
private:
T** matrix;
int sizeX;
int sizeY;
public:
Matrix(int x, int y);
Matrix(Matrix<T>& other);
Matrix(Matrix<T>&& other);
void set_value(T val, int x, int y);
std::optional<Matrix<T>> vec_from_col(int colIndex);
};
template <typename T>
Matrix<T>::Matrix(int x, int y) {
std::cout << "Matrix Param\n";
if (x <= 0 || y <= 0) {
return;
throw -1;
}
else {
matrix = new T*[x];
for (int i = 0; i < x; i++) {
matrix[i] = new T[y];
for (int j = 0; j < y; j++) {
matrix[i][j] = 0;
}
}
sizeX = x;
sizeY = y;
}
}
template <typename T>
Matrix<T>::Matrix(Matrix<T>& other) {
std::cout << "Matrix Copy\n";
sizeX = other.sizeX;
sizeY = other.sizeY;
matrix = new T*[sizeX];
for (int i = 0; i < sizeX; i++) {
matrix[i] = new T[sizeY];
for (int j = 0; j < sizeY; j++) {
matrix[i][j] = other.matrix[i][j];
}
}
}
template <typename T>
Matrix<T>::Matrix(Matrix<T>&& other) {
std::cout << "Matrix Move\n";
sizeX = other.sizeX;
sizeY = other.sizeY;
matrix = other.matrix;
other.matrix = NULL;
}
template <typename T>
void Matrix<T>::set_value(T val, int x, int y) {
if (x < 0 || x >= sizeX || y < 0 || y >= sizeY) {
std::cout << "Invalid index was given. Matrix was unchanged.\n";
return;
}
else {
matrix[x][y] = val;
}
}
template <typename T>
std::optional<Matrix<T>> Matrix<T>::vec_from_col(int colIndex) {
if (colIndex < 0 || colIndex >= sizeX) {
return {};
}
Matrix<T> newVec(1, sizeY);
for (int i = 0; i < sizeY; i++) {
newVec.set_value(matrix[colIndex][i], 0, i);
}
return newVec;
}
template <class T>
Matrix<T> operator!(const std::optional<Matrix<T>>& other) {
return other.value();
}
int main() {
Matrix<int> test(2, 3);
test.set_value(10, 0, 1);
test.set_value(5, 0, 0);
test.set_value(13, 0, 2);
test.set_value(8, 1, 0);
Matrix<int> vec = !test.vec_from_col(0);
}
Your copy constructor is wrong which causes this ambiguity.
Matrix(Matrix<T>& other);
should be
Matrix(const Matrix<T>& other);
Note: You also leak memory since you new[] but don't delete[].

How to access the elements of a column of a matrix using a pointer

I have a matrix A and I need to pass a pointer to a function to access the elements of a given column, here is a part of the code, where PtrColumn should be a pointer to the vector representing the column j of A. Is there a way to get this pointer without calculating the transpose of the matrix A.
vector<vector<double>> A;
int l=4,c=5;
A.reserve(l)
for(size_t i=0;i<l;++i)
{
vector<double> row;
row.reserve(c)
for(size_t j=0;j<c;++j)
row.puch_back(j);
A.puch_back(row);
}
////// some part of the code/////
for(size_t j=0;j<c;++j)
function(PtrColumn) //PtrColumn is a pointer to a vector, and my vector is the column j of A
I would use a matrix class and a wrapper class for the columns:
#include <iostream>
#include <vector>
class Matrix;
class Column {
Matrix *A;
std::size_t column;
public:
Column(Matrix &A, std::size_t column);
double &operator[](std::size_t row);
std::size_t size() const;
};
class Matrix {
std::size_t columns;
std::size_t rows;
std::vector<double> A;
public:
Matrix(std::size_t l, std::size_t r) : columns(r), rows(l), A(l * r) {}
double &operator()(std::size_t i, std::size_t j) { return A[i * columns + j]; }
std::size_t getColumns() const { return columns; }
std::size_t getRows() const { return rows; }
Column getColumn(std::size_t column) { return Column(*this, column); }
};
Column::Column(Matrix &A, std::size_t column) : A(&A), column(column) {}
double &Column::operator[](std::size_t row) { return (*A)(row, column); }
std::size_t Column::size() const { return A->getRows(); }
void function(Column col) {
std::cout << col[2] << '\n';
std::cout << col.size() << '\n';
}
int main() {
int l = 4, c = 5;
Matrix A(l, c);
for(size_t i=0;i<l;++i)
for(size_t j=0;j<c;++j)
A(i, j) = j;
////// some part of the code/////
for(size_t j = 0; j < c; ++j)
function(A.getColumn(j)); //PtrColumn is a pointer to a vector, and my vector is the column j of A
}
Here is a code without matrix class
#include <iostream>
#include <vector>
class Column {
std::vector<std::vector<double>> *A;
std::size_t column;
public:
Column(std::vector<std::vector<double>> &A, std::size_t column) : A(&A), column(column) {}
double &operator[](std::size_t row) { return (*A)[row][column]; }
std::size_t size() const { return A->size(); }
};
void function(Column col) {
std::cout << col[2] << '\n';
std::cout << col.size() << '\n';
}
int main() {
std::vector<std::vector<double>> A;
int l=4,c=5;
A.reserve(l);
for(size_t i=0;i<l;++i) {
std::vector<double> row;
row.reserve(c);
for(size_t j=0;j<c;++j)
row.push_back(j);
A.push_back(row);
}
////// some part of the code/////
for(size_t j=0;j<c;++j)
function(Column(A, j)); //PtrColumn is a pointer to a vector, and my vector is the column j of A
}

C++ Matrix Operator+

The method template <class T> const Matrix<T> Matrix<T>::operator+(const Matrix<T> &rhs) const for program matrix.cc should be able to return the sum of calling object's matrix and rhs's matrix as a new object. Also, the lhs and rhs rows and cols will be equal.
The error output that I am receiving from the compiler is:
[hw7] make clean && make bin/test_add && ./bin/test_add UserSettings ✱
rm -f bin/*
g++ -std=c++11 -Wall -I inc -I src -c src/test_matrix_add.cc -o bin/test_matrix_add.o
g++ -std=c++11 -Wall -I inc -I src -o bin/test_add bin/test_matrix_add.o
Testing Matrix::operator+
Expected Matrix2[0][0]: 3.0, Actual: 1
FAILED
Could someone let me know why I receive this "Failed" output when I know I pass the // TEST MUL ASSIGMENT OP CORRECT RETURN section.
Here is my matrix.cc:
#include <matrix.h>
template <class T>
Matrix<T>::Matrix() {
rows_ = 0;
cols_ = 0;
m_ = nullptr;
}
template <class T>
Matrix<T>::Matrix(unsigned int rows, unsigned int cols)
: rows_(rows), cols_(cols) {
m_ = new T *[rows_];
for (unsigned int i = 0; i < rows_; ++i) {
m_[i] = new T[cols_];
}
}
template <class T>
Matrix<T>::Matrix(const Matrix<T> &that) {
rows_ = that.rows_;
cols_ = that.cols_;
m_ = new T *[rows_];
for (unsigned int i = 0; i < rows_; ++i) {
m_[i] = new T[cols_];
for (unsigned int j = 0; j < cols_; ++j) {
m_[i][j] = that.m_[i][j];
}
}
}
template <class T>
Matrix<T>::~Matrix() {
for (unsigned int i = 0; i < rows_; ++i) {
delete[] m_[i]; // delete columns
}
delete[] m_; // delete columns
}
template <class T>
T Matrix<T>::Get(unsigned int row, unsigned int col) const {
if (row > rows_ && col > cols_) {
throw std::out_of_range("error: index out of range");
}
return this->m_[row][col];
}
template <class T>
const Matrix<T> &Matrix<T>::operator=(const Matrix<T> &rhs) {
if (this == &rhs) {
return *this;
} // returns the address
for (unsigned int i = 0; i < rows_; ++i) {
delete[] m_[i];
}
delete[] m_;
rows_ = rhs.rows_;
cols_ = rhs.cols_;
m_ = new T *[rows_];
for (unsigned int i = 0; i < rows_; ++i) {
m_[i] = new T[cols_];
for (unsigned int j = 0; j < cols_; ++j) {
m_[i][j] = rhs.m_[i][j];
}
}
return *this;
}
template <class T>
const Matrix<T> &Matrix<T>::operator*=(T rhs) {
for (unsigned int i = 0; i < rows_; ++i) {
for (unsigned int j = 0; j < cols_; ++j) {
m_[i][j] *= rhs;
}
}
return *this;
}
template <class T>
const Matrix<T> Matrix<T>::operator+(const Matrix<T> &rhs) const {
if (!(this->cols_ == rhs.cols_) || (this->rows_ == rhs.rows_)) {
std::cout << "Cannont add matrices. Wrong dimensions\n";
exit(0);
}
Matrix<T> lhs;
lhs.rows_ = this->rows_;
lhs.cols_ = this->cols_;
for (unsigned int i = 0; i < lhs.rows_; ++i) {
for (unsigned int j = 0; j < lhs.cols_; ++j) {
lhs[i][j] += rhs[i][j];
}
}
return lhs;
}
Here is my matrix.h:
#include <cassert>
// using assert
#include <exception>
#include <iostream>
template <class T>
class Matrix {
public:
friend class MatrixTester;
Matrix(); // for testing, useless in practice
Matrix(unsigned int rows, unsigned int cols);
Matrix(const Matrix<T> &that);
~Matrix();
T Get(unsigned int row, unsigned int col) const;
const Matrix<T> &operator=(const Matrix<T> &rhs);
const Matrix<T> &operator*=(T rhs);
const Matrix<T> operator+(const Matrix<T> &rhs) const;
private:
T **m_;
unsigned int rows_;
unsigned int cols_;
};
#include <matrix.cc> //NOLINT
This is my test_matrix_add.cc tester:
#include <test_matrix.h>
int main(int argc, char** argv) {
MatrixTester tester;
cout << "Testing Matrix::operator+" << endl;
if (tester.Test_AddOp()) {
cout << " PASSED" << endl;
return 0;
}
cout << " FAILED" << endl;
return 1;
}
bool MatrixTester::Test_AddOp() const {
const int kRows = 4, kCols = 5;
Matrix<double> m1;
m1.m_ = new double*[kRows];
for (unsigned int i = 0; i < kRows; ++i) {
m1.m_[i] = new double[kCols];
for (unsigned int j = 0; j < kCols; ++j)
m1.m_[i][j] = (i + 1.0) * (j + 1.0);
}
m1.rows_ = kRows;
m1.cols_ = kCols;
// TEST ADDITION CORRECTNESS
Matrix<double> m2;
m2 = m1;
// + m1 + m1;
if (m2.m_[0][0] != 3) {
cout << " Expected Matrix2[0][0]: 3.0, Actual: " << m2.m_[0][0] << endl;
return false;
}
if (m2.m_[1][3] != 24.0) {
cout << " Expected Matrix2[1][3]: 24.0, Actual: " << m2.m_[1][3] << endl;
return false;
}
if (m2.m_[2][2] != 27.0) {
cout << " Expected Matrix2[2][2]: 27.0, Actual: " << m2.m_[2][2] << endl;
return false;
}
if (m2.m_[3][4] != 60.0) {
cout << " Expected Matrix2[2][2]: 60.0, Actual: " << m2.m_[2][2] << endl;
return false;
}
return true;
}
Firstly, there is way too much code here.
To address your problem, I see don't see you allocating memory to lhs.m_. This is a problem because you initialize lhs with the default constructor, which only assigns this->m_ to a nullptr.
To fix this, this should work (although untested):
template <class T>
const Matrix<T> Matrix<T>::operator+(const Matrix<T>& rhs) const
{
if (!(this->cols_ == rhs.cols_) || (this->rows_ == rhs.rows_))
{
std::cout << "Cannot add matrices. Wrong dimensions\n";
exit(0);
}
Matrix<T> lhs;
lhs.rows_ = this->rows_;
lhs.cols_ = this->cols_;
// Allocate memory for `lhs.m_`, like you did in your 2nd constructor
lhs.m_ = new T* [rows_];
for (unsigned i = 0; i < rows_; ++i)
{
m_[i] = new T[cols_];
}
// [End] allocation
for (unsigned int i = 0; i < lhs.rows_; ++i)
{
for (unsigned int j = 0; j < lhs.cols_; ++j)
{
lhs[i][j] += rhs[i][j];
}
}
return lhs;
}
Also, somewhat unrelated, be careful that you consistently treat m_ as a double-pointer. I didn't read all your code, but just be cautious. And also remember that you have to deallocate all the memory you allocated with new in your destructor. Personally, I believe you should use smart pointers from <memory> (e.g. std::unique_ptr, etc), which you can learn more about here. Using smart pointers would make the pointers deallocate the memory on their own and you wouldn't have to worry about memory leaks.
Edit 1
As walnut stated, a better solution would be to just call the 2nd constructor, which will allocate the memory for you. So, your revised function would be:
template <class T>
/**
Note:
> When you call this function (e.g. Matrix<T> new_mat = mat1 + mat2),
`mat1` is `this` and `mat2` is what you're calling `rhs`. I've done
some renaming and corrected your logic errors here
*/
const Matrix<T> Matrix<T>::operator+(const Matrix<T>& other) const
{
if (!(this->cols_ == other.cols_) || (this->rows_ == other.rows_))
{
std::cout << "Cannot add matrices. Wrong dimensions\n";
exit(0);
}
// Call the 2nd constructor
Matrix<T> res(this->rows_, this->cols_);
for (unsigned i = 0; i < res.rows_; ++i)
{
for (unsigned j = 0; j < res.cols_; ++j)
{
res.m_[i][j] = this->m_[i][j] + other.m_[i][j];
}
}
return res;
}
Edit 2
The above code has been correct to add the matrices correctly, as per #walnut's comment.

Overloading << operator results in no output

I am not sure if I am correctly overloading the << operator. The following code compiles without problem but does not produce the expected output.
#include <iostream>
#include "Matrix.h"
template<class T>
std::ostream& operator<<(std::ostream &out, const matrix<T> &A)
{
for(int ii = 0; ii << A.size(1); ii++)
{
for(int jj = 0; jj < A.size(2); jj++)
{
out << A(ii,jj) << " ";
}
out << std::endl;
}
return out;
}
int main(int argc, char** argv)
{
matrix<double> A = {{1.0, 2.0},{1.0,-1.0}};
cout << "\"" << A << "\"\n";
return 0;
}
The only output is:
""
Matrix.h
template<class T>
class matrix
{
public:
matrix(int rows, int cols);
matrix(const std::initializer_list<std::initializer_list<T>>& lst);
T& operator()(int i, int j);
T operator()(int i, int j) const;
int size(int n) const;
private:
int mRows;
int mCols;
std::vector<T> mData;
};
template<class T>
matrix<T>::matrix(int rows, int cols)
: mRows(rows),
mCols(cols),
mData(rows * cols)
{
}
template<class T>
matrix<T>::matrix(const std::initializer_list<std::initializer_list<T> >& lst)
: matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int ii = 0, jj = 0;
for(const auto& l : lst)
{
for(const auto& v : l)
{
mData[ii*mCols + jj] = v;
jj++;
}
jj = 0;
ii++;
}
}
template<class T>
T& matrix<T>::operator()(int i, int j)
{
return mData[i*mCols + j];
}
template<class T>
T matrix<T>::operator()(int i, int j) const
{
return mData[i*mCols + j];
}
template<class T>
int matrix<T>::size(int n) const
{
if(n == 1)
{
return mRows;
}
else if(n == 2)
{
return mCols;
}
}
In your first for loop, you have:
for(int ii = 0; ii << A.size(1); ii++)
^
That should be a single < character:
for(int ii = 0; ii < A.size(1); ii++)
^
The reason why it wasn't doing anything is because the original loop resulted in a condition of 0 << A.size(1), which is 0, or false; thus, the outer loop is never executed.