1

I have been working on a matrix class and I have recently learnt about passing const references to operator overloads so that I can have multiple of them on the same line. The problem I encountered is when defining a function for an operator overload, which takes a parameter by const reference, and then tries using another operator overload on that parameter. Some minimal code for this is shown below:

class Matrix
{
private:
    int col, row;
    typedef std::vector<double> Column;
    std::vector<Column> data;
public:
    Matrix(int rows, int columns) : col(columns), row(rows), data(rows, std::vector<double>(columns))
    {}

    Column& operator[](int i) //Operator overload to allow for use of Matrix[i][j]
    {
        return data[i];
    }

    Matrix& operator*(const Matrix& matrix2)
    {
        Matrix* matrix1 = new Matrix(row, col);
        matrix1->data = this->data;
        double tempValue = matrix1[0][0] * matrix2[0][0]; //Error here when accessing matrix2[0][0]

        return *matrix1;
    }
};

As you can see inside the operator* code I am trying to use the overload of the [] operator, which normally helps me use matrix[i][j] notation to enumerate through its data. This was working fine until I started using const Matrix& as a parameter to pass. Now when trying to access the indices of matrix2 it gives me this error:

no operator "[]" matches these operands

Does anyone know why this is happening or how to fix it ? I tried using const int& as a paramter for the operator[] overload but it did not seem to help. Accessing the indices of matrix1 seems to work fine otherwise.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Does your colum class implement an operator[] as well? And in operator*, do not new your return value, return a Matrix not a Matrix*! Avoid new/delete in C++ if you can if you need to allocate memory use std::make_unique (or std::make_shared) – Pepijn Kramer Jul 02 '22 at 09:42
  • 2
    I recommendd [this canonical implementation operator overloading guide](https://en.cppreference.com/w/cpp/language/operators#Canonical_implementations). – Some programmer dude Jul 02 '22 at 09:44

1 Answers1

1

For starters matrix1 is a pointer but you need to apply the subscript operator for an object of the type Matrix.

matrix2 is a constant object but the subscript operator is not a constant member function.

You need to overload the operator [] as a constant member function

Column& operator[](int i) //Operator overload to allow for use of Matrix[i][j]
{
    return data[i];
}

const Column& operator[](int i) const //Operator overload to allow for use of Matrix[i][j]
{
    return data[i];
}

And the operator * should be declared like

Matrix operator*(const Matrix& matrix2) const
{
    Matrix matrix1(row, col);
    matrix1.data = this->data;
    double tempValue = matrix1[0][0] * matrix2[0][0];

    return matrix1;
}

though it is unclear what this statement

    double tempValue = matrix1[0][0] * matrix2[0][0];

is doing here.:)

Pay attention to that dynamically allocating an object in the operator * is a very bad idea.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • The problem I have here is that if I return matrix1 by value from the * operator, I won't be able to use several of these operators on one line without assigning to intermediate variables. For example, Matrix matrix = matrix1 * matrix2 * matrix3 will not work unless the * operator returns a matrix object by reference. The tempValue statement is not doing anything functional by the way, I stripped the function to only show where I am encountering an error, it actually loops around the indices and does standard matrix multiplication. – AssistantToTheRegionalManager Jul 02 '22 at 10:05
  • 1
    @AssistantToTheRegionalManager You are mistaken. You may bind a temporary object to a constant reference. If you will make the changes I showed you will be able to write for example Matrix m1( 2, 2 ); Matrix m2 = m1 * Matrix( 2, 2 ) * Matrix( 2, 2 ); – Vlad from Moscow Jul 02 '22 at 10:06
  • Hmm it seems that I can indeed bind it but can you explain it a bit more ? Everywhere else I've read that I need to return by reference not by value if I want to be able to chain operator overloads on single line like that. – AssistantToTheRegionalManager Jul 02 '22 at 10:16
  • 1
    @AssistantToTheRegionalManager References should be returned by assignment operators or by subscript operators at least that are not constant member functions. – Vlad from Moscow Jul 02 '22 at 10:17
  • Yes that does make more sense to me now. The thing I was wondering is for the * operator, does changing the input parameters from (Matrix matrix2) to (const Matrix& matrix2) alone allow us to bind the returned temporary object to the passed const reference ? I.e. there's no need to return a pointer as the temp object lifetime is stretched to the lifetime of the input variable ? – AssistantToTheRegionalManager Jul 02 '22 at 10:27
  • 1
    @AssistantToTheRegionalManager You may bind a temporary object to a constant lvalue reference. So you may pass a temporary object to a function that has the corresponding parameter as a constant reference and the temporary object will be alive while the function is executed. – Vlad from Moscow Jul 02 '22 at 10:29
  • I see, thanks for the help, this is really neat. There was only one more problem I encounter with this. After changing the [] operator to return const Column& as you suggested, I can no longer assign to matrix indices as matrix[i][j] = value. It now says that it needs to be a modifiable lvalue, which is understandable, since it returns a const reference. As this is a functionality that's really useful, is there a way to do this while using this new solution for the * operator ? – AssistantToTheRegionalManager Jul 02 '22 at 10:43
  • 1
    @AssistantToTheRegionalManager You need to have two overloaded operators as I showed in my answer. – Vlad from Moscow Jul 02 '22 at 10:44