Reputation: 1499
I want to override parent class overloaded operators but I'd like to avoid boilerplate code rewriting all non-member operators for inherited class. Is it possible at all?
In the following example, I overloaded virtual Foo & Foo::operator+=(Foo const &)
and based a free function Foo & operator+(Foo, Foo const &)
out of it. In Bar, I overrode Bar & Bar::operator+=(Foo const &) override
. What I want is the free function to call the overriden function when I state Bar + Foo
and I expect Foo
as a result. I know that overloading again Bar operator+(Bar, Foo const &)
solves for that particular situation but I'd like to avoid explicitly do that if possible (think about all the other operators). And then there's also Foo + Bar
that I want to return Bar
.
#include <iostream>
class Foo {
public:
Foo(unsigned int bottles=11) : bottles(bottles) {} // This is odd on purpose
virtual void display(std::ostream & out) const {
out << bottles << " bottles";
}
virtual Foo & operator+=(Foo const &);
protected:
unsigned int bottles;
};
std::ostream & operator<<(std::ostream & out, Foo const & f) {
f.display(out);
return out;
}
Foo & Foo::operator+=(Foo const &f) {
bottles += f.bottles;
return *this;
}
Foo const operator+(Foo f, Foo const & g) {
return f += g;
}
class Bar : public Foo {
public:
Bar(unsigned int bottles=0) : Foo(bottles) { enforce(); }
Bar(Foo const & f) : Foo(f) { enforce(); }
void display(std::ostream & out) const override {
out << bottles << " manageable bottles";
}
Bar & operator+=(Foo const &) override;
private:
void enforce() { bottles /= 2; bottles *=2; }
};
Bar & Bar::operator+=(Foo const &f) {
Foo::operator+=(f);
enforce();
return *this;
}
int main () {
std::cout << "----- Foo + Foo -----" << std::endl;
Foo bar;
Foo becue(2);
std::cout << bar << " + " << becue << " -> (+) "
<< bar + becue << std::endl;
std::cout << "----- Bar + Bar -----" << std::endl;
Bar crazy(bar);
Bar horse(5);
std::cout << crazy << " + " << horse << " -> (+) "
<< crazy + horse << std::endl;
std::cout << "----- Bar + Foo -----" << std::endl;
std::cout << crazy << " + " << bar << " -> (+) "
<< crazy + bar << std::endl;
std::cout << "----- Foo + Bar -----" << std::endl;
std::cout << bar << " + " << horse << " -> (+) "
<< bar + horse << std::endl;
return 0;
}
I expect manageable bottles as a result each time manageable bottles are involved.
Upvotes: 2
Views: 138
Reputation: 133629
The problem derives from object slicing that occurs when invoking
Foo const operator+(Foo f, Foo const & g) {
return f += g;
}
Here, f
is passed by value, which means that any additional information of subtypes of Foo
are discarded. So the compiler just sees a Foo
and is not able to call the polymorphic operator.
To prevent slicing you are forced to pass a pointer or a reference, but this would imply that you need an l-value
as first operand and you can't use const
because you are calling operator+=
on it.
So you could have
Foo const operator+(Foo& f, Foo const & g) {
return f += g;
}
and it would work for your specific situation like:
Foo bar;
Bar crazy(bar);
std::cout << crazy + bar << std::endl;
Because crazy
is an l-value
but you won't be able to do Bar(5) + horse
nor Foo(5) + horse
.
Upvotes: 1