I have a class that represents a diagonal matrix. I only store the elements along the diagonal, so I don't waste space with a bunch of 0's. However, I still want to be able to use double brackets to access elements in the array. To get around that, I use an inner class, like this:
template <class T>
class DiagonalMatrix
{
private:
const T ZERO = 0;
int _size;
Vector<T> _data;
class row
{
private:
DiagonalMatrix<T>* _parent;
int _row;
public:
row(DiagonalMatrix<T>* parent, const int row)
: _parent(parent), _row(row) {}
T& operator[](const int i);
};
class const_row
{
private:
const DiagonalMatrix<T>* const _parent;
int _row;
public:
const_row(const DiagonalMatrix<T>* const parent, const int row)
: _parent(parent), _row(row) {}
const T& operator[](const int i) const;
};
friend class row;
friend class const_row;
public:
row operator[] (const int i);
const const_row operator[] (const int i) const;
// other stuff
};
And here are the relevant definitions:
template<class T>
typename DiagonalMatrix<T>::row DiagonalMatrix<T>::operator[](const int i)
{
if (i < 0 || i >= _size)
{
throw IndexOutOfBoundsException(i);
}
return DiagonalMatrix<T>::row(this, i);
}
template <class T>
T& DiagonalMatrix<T>::row::operator[](const int i)
{
if (i < 0 || i >= _parent->_size)
{
throw IndexOutOfBoundsException(i);
}
if (row == col)
{
return _parent->_data[row];
}
// TODO Add a real exception
throw "Cannot modify non-diagonal elements";
}
With similar definitions for the const versions, except the const operator[] returns a reference to the constant ZERO instead of throwing for non-diagonal elements.
So here is my problem: The non-const version is being called even when I don't need to modify anything. For example, this throws my error string:
DiagonalMatrix<double> diag(5);
// fill in the diagonal elements with some values
cout << diag[0][2] << endl;
However, if I remove the non-const versions of the operator, it behaves as expected and outputs a 0.
I've also tried something like:
T& at(const int row, const int col);
const T& at(const int row, const int col) const;
// in main:
cout << diag.at(0, 2) << endl;
But this has the same issue. So I have two questions:
1) Why does C++ choose the non-const version of the function over the const version even when I am not assigning to the result? Doesn't operator<< typically pass the right-hand object by const&?
2) How can I get around this? I'd rather not have separate get() and set() functions if I can help it.