3

I have the following code:

class Rectangle
{
protected:
    int a, b;
public:
    Rectangle(int a, int b) : a(a), b(b) {}
    int area() { return a*b; }
    Rectangle operator+(const Rectangle & other)
    {
        Rectangle temp(0, 0);
        temp.a = a + other.a;
        temp.b = b + other.b;
        return temp;
    }
    void operator=(const Rectangle & other)
    {
        a = other.a;
        b = other.b;
    }
};

class Square : public Rectangle
{
public:
    Square(int a) : Rectangle(a, a) {}
};

int main()
{
    Square s1(3);
    Square s2(1);
    cout << (s1 + s2).area();
    s1 = s1 + s2;
}

The cout << (s1 + s2).area(); is OK but at s1 = s1 + s2; compiler gives me an error:

no match for 'operator=' (operand types are 'Square' and 'Rectangle')

Why this line does not work?

michalt38
  • 1,193
  • 8
  • 17
  • 1
    must be `Rectangle & operator=(const Rectangle & other)` – bruno Apr 23 '20 at 09:37
  • @bruno How will that solve the issue, that is described? – Algirdas Preidžius Apr 23 '20 at 09:39
  • I was not clear; the signature is not the right one for rectangle, and it is needed to also define the operator in Square, there is no inheriting for operator – bruno Apr 23 '20 at 09:40
  • @bruno That has nothing to do with the problem, and OP's code is valid but unexpected. – molbdnilo Apr 23 '20 at 09:40
  • 6
    Note that it is generally *not correct* to model a square as inheriting from a rectangle. Such an inheritance chain violates the [Liskov substitution principle](https://stackoverflow.com/a/59006/1968). – Konrad Rudolph Apr 23 '20 at 09:43
  • 3
    Do you really want to be able to write `Square s; ... s = Rectangle(1,1000);`? – molbdnilo Apr 23 '20 at 09:45
  • @molbdnilo it is not valid, it does not allow a sequence of assignment a = b = ... – bruno Apr 23 '20 at 09:45
  • @bruno It is perfectly valid as long as you don't do that. That's a convention, not a rule. – molbdnilo Apr 23 '20 at 09:46
  • @molbdnilo for your previous remark you are 100% right – bruno Apr 23 '20 at 09:46
  • 2
    Assignment is not the only problem; `Rectangle(1,1)` is a square `Rectangle`, but it's not a `Square`. Thus, you can have square rectangles that are not squares, and you can have squares that are not square rectangle. I think the only sensible way to model this is to make "being square" a property of rectangles instead of making squares a separate type. – molbdnilo Apr 23 '20 at 10:04
  • To augment @molbdnilo 's suggestion, you can still declare `Rectangle square(int a) { return Rectangle(a, a); }` – Caleth Apr 23 '20 at 11:05

1 Answers1

6

If you don't provide an assignment operator, a compiler will declare one for you. Here, a compiler generates Square::operator=(const Square&). Assignment operator from Rectangle gets hidden by this operator. You can bring Rectangle::operator= into the scope with using declaration:

class Square : public Rectangle
{
public:
    using Rectangle::operator=;

    // ...
};

Now the code compiles, but it is flawed. As noted in comments by Konrad Rudolph, Jarod42, and molbdnilo, deriving Square from Rectangle is logically wrong, as it violates the Liskov's substitution principle. You can now assign a Rectangle to a Square, and such an assignment doesn't make sense.

Evg
  • 25,259
  • 5
  • 41
  • 83
  • 3
    Which is logical wrong, `Rectangle r(1, 3); Sqaure s(1); s = r;`... with a square, not square. – Jarod42 Apr 23 '20 at 09:45
  • If I would create base class Shape and inherit Square from the Shape, would it be sensible to use Shape's assignment operator in a Square class? Or using operator= of the base class in derived class is bad idea at all? – michalt38 Apr 23 '20 at 14:08
  • @michalt38, in the general case it is not a bad idea. Sometimes that's exactly what you need. – Evg Apr 23 '20 at 14:21