Covariant return types and rvalues references

217 Views Asked by At

I aim to implement an operator on derived classes such that the return type should be of the derived class.

I was able to achieve it with the following code:

#include <iostream>

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


class Derived : public Base{
public:
  Derived&& operator+ (const Base &b) override
  {
    Derived *res = new Derived;
    auto tmp = dynamic_cast<const Derived*>(&b);
    if(tmp) res->fVal = this->fVal+tmp->fVal;
    return std::move(*res);
  }
  
  void SetVal(double val){fVal = val;}
  void Val() override{std::cout<<"derived:"<<fVal<<std::endl;}
private:
  double fVal{0};
};

int main(){
  Derived a;
  a.SetVal(2);
  Derived b;
  b.SetVal(3);
  Base *baseptr = new Derived(a+b);
  baseptr->Val();
  Base &&d = a+b;
  d.Val();
  Derived e = dynamic_cast<Derived&>(d);
  e.Val();
  return 0;
}

I am not really confortable with using an rvalue-reference for the return type, so I would like to know if there is a simpler solution that I cannot see. Returning a copy of the derived type didn't work, as gcc states that the return type is not covariant.

EDIT: As @ofo has mentioned, I could achieve something quite similar returning a lvalue-reference. For now it is the option that suits me the best. However, it is still undesirable (to say the least) that there is a memory leak waiting to happen, I just need to not do anything with the result of a+b;

1

There are 1 best solutions below

8
orlandini On

I don't see a way of achieving this without returning a pointer, as suggested by @Eljay. Returning a reference could work but I see no way of ensuring that the caller will delete whatever needs to be deleted.

I thought of this (where func represents a scenario in which I would use the operator):

#include <iostream>
#include <memory>

class Base{
public:
  virtual std::unique_ptr<Base> operator+(const Base &) = 0;
  virtual void SetVal(double) = 0;
  virtual void Val() = 0;
};


class Derived : public Base{
public:
  std::unique_ptr<Base> operator+(const Base &b) override
  {
    std::unique_ptr<Base> res = std::make_unique<Derived>();
    auto tmp = dynamic_cast<const Derived*>(&b);
    if(tmp) res->SetVal(this->fVal+tmp->fVal);
    else std::cout<<"now i deal with invalid type case"<<std::endl;
    return res;
  }
  
  void SetVal(double val) override{fVal = val;}
  void Val(){std::cout<<fVal<<std::endl;}
private:
  double fVal{0};
};

void func(Base &a, Base &b)
{
  auto res = a+b;
  res->Val();
}

int main(){
  Derived a;
  a.SetVal(2);
  Derived b;
  b.SetVal(3);
  auto d = a+b;
  d->Val();

  func(a,b);
  return 0;
}

I will leave this up for a while in case someone finds a better solution. Otherwise I will mark it as an answer.