Question

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.

Was it helpful?

Solution

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&?

It choose the non-const version because diag is not a const object.

2) How can I get around this? I'd rather not have separate get() and set() functions if I can help it.

You can define a const refrence to diag and use it to print values:

const DiagonalMatrix<double> &const_diag = diag;
cout << diag[0][2] << endl; // call const version

Or you define a function to print values and pass a const reference in:

void show_diag(const DiagonalMatrix<double> &diag)
{
    cout << diag[0][2] << endl; // call const version
}

show_diag(diag);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top