Is there a way to overload the operators in the hierarchy, which leads to what I'm going to do here?
No.
The problem is that the operators are not in your hierarchy. The friend
keyword here is simply forward-declaring a free function and giving it privileged access to the class. This does not make it a method, so it cannot be virtual.
Note that operator overloading is just syntactic sugar. Expression
os << shape;
can either go to a free function (like you are here)
ostream& operator<< (ostream&, Polygon&);
or a member of the left operand, for example
ostream& ostream::operator<<(Polygon&);
(obviously, the second case does not exist here, because you will need to modify std::ostream
). That the syntax cannot resolve is a member of the right operand.
So, you can have a free function operator (which is not necessarily virtual) or a method in the left operand (which can be virtual), but not a method in the right operand.
The usual solution is to have one overload for the top level of the hierarchy that is sent to the virtual method. So:
class Polygon { public: virtual ostream& format(ostream&); }; ostream& operator<<(ostream& os, const Polygon& p) { return p.format(os); }
Now we simply implement Polygon::format
and redefine it in derived classes.
Aside, using friend
still carries the smell of code. In general, he believed that a better style gives your class a public interface complete enough so that external code does not need privileged access to work with it.
A further digression for reference information: multiple sending is a thing, and C ++ overload resolution does an excellent job when all types of arguments are known statically. What is not being processed is to search for the dynamic type of each argument at runtime, and then try to find the best overload (which, if you count multiple class hierarchies, is clearly non-trivial).
If you replace the polymorphic run-time list with the polymorphic compile-time tuple and go to it, the original overload scheme will be sent correctly.