Search code examples
c++inheritanceprototypal-inheritance

Can't overload the = assignment operator in derived class (as I want); call from main jumps to base class operator method


I'm trying to implement a class for a matrix with all the usual operations, just as an exercise. I implemented the common operations (+, -, ·) but my problem came when I tried to implement slicing operations—similar to Numpy. For example, if I want to make something like a[2:5,1:3] = b[10:13, 10:12] I concluded that I would need some kind of ‘smart iterator’ (I don't know the technical name) that detect how to move one row down. I have two classes:

  • MatrixIterator: A base class with a pointer and indexes to the sub-matrix they represent (could be the whole matrix).
  • Matrix: The derived class of MatrixIterator, which has almost the same operators inherited, with the difference that in the constructor and deconstructor is allocated and released the part of the memory which contains the matrix array.

The problem arose when I realized that the = assignment operator should be different for both classes. Because If I want to make something like a = b, then I should just destroy the allocated array in a and make a ‘re-initialization’ with the data of b (in case the dimensions changed); which, as I understood, must be ‘manually’ done. But with a[2:5,1:3] = b[10:13, 10:12], I just assign element to element without a ‘re-initialization’.

I ended up with something like

#include <stdexcept>
#include <bits/stdc++.h>
#include <iostream>

//Base class
template <class T>
class MatrixIterator
{
    public:
        MatrixIterator<T>();
        MatrixIterator<T>& operator=(const MatrixIterator<T>& b);
};

//Derived class
template <class T>
class Matrix : public MatrixIterator<T>
{
    using base = MatrixIterator<T>;
    public:
        Matrix<T>();
        Matrix<T>& operator=(const MatrixIterator<T>& b);
};

template <class T>
MatrixIterator<T>::MatrixIterator()
{
}

template <class T>
MatrixIterator<T>& MatrixIterator<T>::operator=(const MatrixIterator<T>& b)
{
    std::cout << "Base class method called" << std::endl;
    return *this;
}

template <class T>
Matrix<T>::Matrix()
{
}

template <class T>
Matrix<T>& Matrix<T>::operator=(const MatrixIterator<T>& b)
{
    std::cout << "Derived class method called" << std::endl;
    return *this;
}

int main()
{
    Matrix<int> a;
    Matrix<int> c;
    c = a;

    return 0;
}

Which outputs Base class method called.

Problem: When I do c = a, the program jumps to the base class MatrixIterator = method, while I'm expecting that it jump to the derived class Matrix method, becuse a was declared as such.

So, I changed the prototype of the function from Matrix<T>& operator=(const MatrixIterator<T>& b); to Matrix<T>& operator=(const Matrix<T>& b) and... it works fine. But I don't want that because I want to implement the = operator with the right operand understood in a broader sense; for it could be either a Matrix (a = b) or a MatrixIterator (a = b[10:13, 10:12]), both assignments requiring the left operand to be ‘re-initialized’.

I could write four methods for the four combinations of the = operator (Matrix = Matrix, Matrix = MatrixIterator, MatrixIterator = Matrix, MatrixIterator = MatrixIterator). But I'm too lazy for doing that. Actually, the whole point in creating the MatrixIterator as a base class and defining almost all the operators in that class was with the purpose of avoiding to rewrite the implementation of every operator four times.

So: If somebody understands what I'm trying to do, is there a way to make the compiler understand me? Does it worth it? Or would you say that my implementation of a base class as iterator is a poor , naïf, weak-minded strategy? Don't hesitate, say whatever you think.


Solution

  • One way is to overload assignment operator=(Matrix<T>) and then delegate the task to the operator=(MatrixIterator<T>) as shown below.

    //Derived class
    template <class T>
    class Matrix : public MatrixIterator<T>
    {
        using base = MatrixIterator<T>;
        public:
            Matrix();
            Matrix<T>& operator=(const MatrixIterator<T>& b);
            //ADDED THIS OVERLOAD
            Matrix<T>& operator=(const Matrix<T>& b)
            {
                std::cout << "Derived class Matrix parameter method called"<<std::endl;
                return (this->*static_cast<Matrix<int>& (Matrix<int>::*)(const MatrixIterator<int>&)>(&Matrix<int>::operator=))(b);//you can change this accordingly
            }
    };
    

    Live demo