Virtual operator +
can work in general, however for it to work, some constraints must be met.
The first reason why it would not work in your case is, that the operator
BigIntegerNumber operator+ (BigIntegerNumber row2)
is not an override of the
virtual CharRow operator+ (CharRow row2)
but it is its overload (hides the original operator instead of overriding it).
For it to be the override, the function signatures would have to be the same. I.e. the same types of parameters (CharRow
and not BigIntegerNumber
, also better to pass by const ref than by value), and the same or covariant return type. Neither of those are met.
Things like this are sometimes done by using a "regular" virtual function taking interface references as parameters, and implementing non-virtual operators by calling such func.
In this case the main issue is the return parameter, as you cannot define the return type as by-value CharRow
and actually return BigIntegerNumber
(it would be sliced to the return type). You might be more lucky with the operator +=
, which can return the reference to itself thus be able to work polymorphically.
The example for operator +=
(which does not have the problem with the return value type):
#include <iostream>
using namespace std;
struct Base
{
virtual Base& operator +=(const Base& other); // takes Derived as well for the virtual calls
};
struct Derived: Base
{
Derived& operator +=(const Base& other); // override - called via virtual
Derived& operator +=(const Derived& other); // overload - not called via virtual
// remove to always call the polymorphic version
};
Base& Base::operator +=(const Base& other)
{
cout << "Base::operator +=(Base)";
// beware this is slow!
const Derived* d = dynamic_cast<const Derived*>(&other);
if (d)
cout << " - called with Derived";
else
cout << " - called with Base";
cout << endl;
return *this;
}
Derived& Derived::operator +=(const Base& other)
{
cout << "Derived::operator +=(Base)";
// beware this is slow!
const Derived* d = dynamic_cast<const Derived*>(&other);
if (d)
cout << " - called with Derived";
else
cout << " - called with Base";
cout << endl;
return *this;
}
Derived& Derived::operator +=(const Derived& other)
{
cout << "Derived::operator +=(Derived)" << endl;
return *this;
}
int main()
{
Derived d1, d2;
Base b, b0;
Base& b1 = d1;
Base& b2 = d2;
d1 += d2; // Derived::operator +=(Derived)
b1 += d2; // Derived::operator +=(Base) - called with Derived
d1 += b1; // Derived::operator +=(Base) - called with Derived
b1 += b2; // Derived::operator +=(Base) - called with Derived
b += d2; // Base::operator +=(Base) - called with Derived
d1 += b; // Derived::operator +=(Base) - called with Base
b += b0; // Base::operator +=(Base) - called with Base
b1 += b; // Derived::operator +=(Base) - called with Base
return 0;
}
For the operator +
the result type passed by value is the problem. However, in C++ still not impossible, but you then need to use some kind of wrapper. An example of such a wrapper:
#include <iostream>
#include <memory>
using namespace std;
struct Base;
struct Derived;
class BaseWrapper
{
shared_ptr<Base> _obj;
public:
explicit BaseWrapper(const shared_ptr<Base>& obj) : _obj(obj)
{}
template<class RESULT_T>
operator RESULT_T()
{
// throws if type not correct
return dynamic_cast<RESULT_T&>(*_obj);
}
Base& operator +=(const Base& other);
BaseWrapper operator +(const Base& other) const;
};
struct Base
{
virtual Base& operator +=(const Base& other); // takes Derived as well for the virtual calls
BaseWrapper operator +(const Base& other) const;
private:
virtual shared_ptr<Base> copy() const
{
return make_shared<Base>(*this);
}
};
struct Derived : Base
{
Derived& operator +=(const Base& other); // override - called via virtual
private:
virtual shared_ptr<Base> copy() const
{
return make_shared<Derived>(*this);
}
};
Base& BaseWrapper::operator += (const Base& other)
{
return *_obj += other;
}
BaseWrapper BaseWrapper::operator +(const Base& other) const
{
return *_obj + other;
}
BaseWrapper Base::operator +(const Base& other) const
{
BaseWrapper result(copy());
result += other;
return result;
}
int main()
{
Derived d1, d2;
Base b, b0;
Base& b1 = d1;
Base& b2 = d2;
b = b1 + b2; // add Derived + Derived, result is Derived (typed Base)
b = b0 + d1; // add Base + Derived, result is Base
// d1 = b0 + d1; // add Base + Derived, result is Base, throws bad_cast (cannot cast to Derived)
d1 = b1 + b2; // add Derived + Derived, result is Derived
return 0;
}
That is, the BaseWrapper can be used to return the polymorphic type by value, and have conversions to the original type. But also note that in this case the memory allocation is involved.