Implicit C ++ Transformation Operators

I am trying to find good inheritance in C ++.

I have a Rectangle class and a square class. The Square class cannot publicly inherit a Rectangle, because it cannot fully comply with the rectangle requirements. For example, a rectangle can have a width and a height, each of which is set separately, and this, of course, is impossible with a square.

So, my dilemma. The square will obviously share a lot of code with the Rectangle; they are very similar.

For example, if I have a function like:

bool IsPointInRectangle(const Rectangle& rect); 

It should work on the square. In fact, I have a ton of such features.

Thus, when creating the Square class, I decided to use private inheritance with the public Rectangle transform operator. So my square class looks like this:

 class Square : private Rectangle { public: operator const Rectangle&() const; }; 

However, when I try to pass the square to the IsPointInRectangle function, my compiler simply complains that, in this context, "The rectangle is an inaccessible base." I expect him to notice the Rectangle operator and use it instead.

Am I trying to make it even possible?

If that doesn't work, I'm probably going to refactor the Rectangle part into a MutableRectangle class.

Thanks.

+6
c ++ inheritance reference
source share
3 answers

Ok, I'm surprised. It seems that the private inheritance of class A does not allow you to use the operator A outside the class.

You can solve your problem by making a Rectangle element for the square and using it to translate:

 class Square { Rectangle r; public: operator const Rectangle&() const { return r; } }; 

This should compile and work. And I believe that this will not give you much more work, if any.

+3
source share

You can create an ImmutableRectangle class without any mutators and only with const methods, from which you can correctly derive both Rectangle and, separately, ImmutableSquare and, from this, Square . Note that with reduced variability, the IS-A relation has an IS-A constant square, an immutable rectangle: variability is the only serious problem, therefore, decomposing it, you can get some significant reuse code (for all const uses those that are do not actually use or do not need variability).

Introducing variability along inheritance is in order if no invariants of the class of (immutable) base actually depend on the characteristics of immutability; and, of course, an immutable object can be correctly constructed from a const pointer or a link to a mutable version (presumably in a separate built-in function of a friend, to avoid giving the base class a dependency on the derived class ;-) for reasonable use.

Edit : one comment, for obvious reasons, expresses remorse because “mutabe is not immutable”: you need to talk about it to understand what “IS-A” means ... and it does not mean Korzybski -denied " is identity" : it means LSP . Go through the rigmarole of restrictions, this means: covariance, contravariance, weaker equal prerequisites, stronger postconditions, etc., since they apply to the const methods of the base (immutable) and derivative (mutable) classes. You will see that class invariants are the only problem, as I mentioned in the previous paragraph, so just avoid asserting immutability as an invariant of the class, and you are in clover; -).

Perhaps this will help to name the base class NotNecessarilyMutableRectangle , since it does not claim immutability as an invariant of the class; that a very accurate name may be philosophically encouraging, but perhaps a trifle, unsuitable in everyday coding.

+6
source share

I believe, although I'm not sure, you need to use an explicit order to invoke this conversion operator in this context. The ImmutableRectangle base is a common and effective solution. Similarly, you can use a more abstract solution, for example:

 /** * Base for rectangular classes; name it whatever you want. */ class RectangularBase { public: virtual unsigned int getValue(int) const = 0; }; /** * Base for specific rectangular classes; also named whatever. */ template<unsigned int Dimensions> class Rectangular : public RectangularBase { public: virtual unsigned int getValue(int index) const { return values[index]; } private: unsigned int values[Dimensions]; }; /** * A square is Rectangular but has only one significant dimension. */ class Square : public Rectangular<1> { public: unsigned int getSideLength() const { return getValue(0); } }; /** * A Rectangle is Rectangular and has two significant dimensions. */ class Rectangle : public Rectangular<2> { public: unsigned int getWidth() const { return getValue(0); } unsigned int getHeight() const { return getValue(1); } }; 
0
source share

All Articles