How do I declare an overloaded operator in an abstract class and override it in a derived non-abstract class?
Asked Answered
F

3

9

I'm trying to write an abstract class with some pure virtual binary operators, which should be implemented by the derived class in order to accomplish operator polymorphism. Here's a simplified example:

class Base {
public:
    virtual const Base& operator+ (const Base&) const = 0;
};

class Derived : public Base {
public:
    const Derived& operator+ (const Derived&) const;
};

const Derived& Derived::operator+ (const Derived& rvalue) const {
    return Derived();
}

It doesn't matter right now what the operator does, the important part is what it returns: it returns a temporary Derived object, or a reference to it. Now, if I try to compile, I get this:

test.cpp: In member function ‘virtual const Derived& Derived::operator+(const Derived&) const’:
test.cpp:12:17: error: cannot allocate an object of abstract type ‘Derived’
test.cpp:6:7: note:   because the following virtual functions are pure within ‘Derived’:
test.cpp:3:22: note:    virtual const Base& Base::operator+(const Base&) const

What's wrong? Isn't operator+ (the only pure virtual function in Base) being overriden? Why should Derived be abstract as well?

Friede answered 21/10, 2012 at 14:10 Comment(2)
Looks like it's because the functions have different signatures. const Derived& Derived::operator+ (const Derived& rvalue) const vs virtual const Base& Derived::operator+ (const Base&) constPledgee
By the way, you must not return a reference to a temporary object, because the referand's lifetime is over as soon as the function returns. operator+ needs to return by value, which rules out returning a Derived when your caller expects a Base.Schweitzer
A
4

This kind of overloading is not possible with a normal abstract class in a clean way. First: you should declare + as non member Overload operators as member function or non-member (friend) function?.

The best you can get is to inherit from a templated interface if you really need this functionality:

template<typename T>
class AddEnabled {
  public:
    friend T operator+ (T const& left, T const& right) {
      return left+=right;
    }
};

Now you write

class Foo: public AddEnabled<Foo>{
  Foo():mVal(0){
  }

  Foo& operator+=(Foo const& foo){
    mVal+=foo.mVal;
  }

 private:
  int mVal;
}

If you comment out Foo& operator+=(Foo const& foo){ you will get a compiler error saying that the operator is not implemented. If you want to know more about the principles involved lookup http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick and http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

HTH, Martin

Aldine answered 21/10, 2012 at 14:33 Comment(2)
So, I guess here in case you have multiple derived classes (e.g., Foo and Bar) it may make sense for them to implement both AddEnabled<Foo> and AddEnabled<Bar>. However, how would you treat the case where the implementation of a Foo+Foo is different from the one for Foo+Bar or Bar+Bar etc?Selmore
@PantelisSopasakis: That depends. If Foo and Bar have no inheritance relation you can simply implement the functions as non member (and perhaps non friend functions) and be done with it. If you have inheritance between them, I would definitely avoid doing this via operator overloading. Your code will become very error prone in no timeAldine
B
6

Although the return type in Derived can be co-variant to the base one, you can't do the same with the argument types. I.e., the overriding function should look like this:

class Derived : public Base 
{ 
public: 
    const Derived& operator+ (const Base&) const; 
}; 
Beauharnais answered 21/10, 2012 at 14:18 Comment(0)
A
4

This kind of overloading is not possible with a normal abstract class in a clean way. First: you should declare + as non member Overload operators as member function or non-member (friend) function?.

The best you can get is to inherit from a templated interface if you really need this functionality:

template<typename T>
class AddEnabled {
  public:
    friend T operator+ (T const& left, T const& right) {
      return left+=right;
    }
};

Now you write

class Foo: public AddEnabled<Foo>{
  Foo():mVal(0){
  }

  Foo& operator+=(Foo const& foo){
    mVal+=foo.mVal;
  }

 private:
  int mVal;
}

If you comment out Foo& operator+=(Foo const& foo){ you will get a compiler error saying that the operator is not implemented. If you want to know more about the principles involved lookup http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick and http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

HTH, Martin

Aldine answered 21/10, 2012 at 14:33 Comment(2)
So, I guess here in case you have multiple derived classes (e.g., Foo and Bar) it may make sense for them to implement both AddEnabled<Foo> and AddEnabled<Bar>. However, how would you treat the case where the implementation of a Foo+Foo is different from the one for Foo+Bar or Bar+Bar etc?Selmore
@PantelisSopasakis: That depends. If Foo and Bar have no inheritance relation you can simply implement the functions as non member (and perhaps non friend functions) and be done with it. If you have inheritance between them, I would definitely avoid doing this via operator overloading. Your code will become very error prone in no timeAldine
C
1

FYI the current answers are a bit outdated. For C++14, through at least C++20, you can so something like this:

class Base {
public:
    virtual const auto operator+(const Base&) const -> decltype(*this) = 0;
};

class Derived : public Base {
public:
    const Derived& operator+(const Base&) const override;
};

This is going to avoid the compilation issue resultant from attempting to specify an abstract class type as a return value in your Base class. Also, as noted in other answers, the argument of your derived function should be the same as in Base in order to perform a proper override.

Councilwoman answered 1/4, 2022 at 21:30 Comment(1)
What if the + runs on a member of Derived, which Base doesn't have?Midinette

© 2022 - 2024 — McMap. All rights reserved.