Question

I have an abstract base class and a templated derived class. Derived objects can be constructed by a previous instance of a derived object and, say, an integer. So far we have

struct base {
  /* ...pure virtual functions here... */

  virtual ~base() = default;
  /* NO DATA */
};

template <class D>
struct derived : base {
  derived() = default;

  derived(int x, const derived& previous) {
    /* to construct I need access to previous.data_ */
  }

  /* ...overriden virtual functions here... */

  ~derived() = default;

  D data_;
};

Please note that the constructor of the derived class requires access to the data_ member of the previous derived object. Now I want to create a function which will construct objects of type derived<D> by taking as input an integer and a previous instance of derived<D>, and return a pointer to base. The problem is that since the user will be working with base class pointers, the function should look like this:

template <class D>
std::shared_ptr<base> getNext(int x, std::shared_ptr<base> current) {
  return std::make_shared<derived<D>>(x, *current); /* ERROR */
}

This, as you may have guessed, creates a compilation error saying that there is no known conversion from base to derived<D>. One thing I know I could use is a static_cast<derived<D>&>(*current) since the type of the underlying object will always be derived<D>, but ideally I would like to avoid any casts if possible. Any ideas how to overcome this problem>? Thanks in advance!

Was it helpful?

Solution 2

When you're working with a polymorphic class and find yourself needing to cast to the derived type, this is often a clue that something is wrong with the design. After all, why have virtual functions if you still need to get to the derived class? I'm not saying there are no exceptions to this -- but I will say they are few and far between.

In this case, you (think you) need to cast in order to clone the object. Instead, I'd provide a clone method on the base class (here called getNext). Make that virtual, override it in the derived class, and call that instead:

struct base {
public:
  virtual std::shared_ptr<base> getNext(int x) = 0;
};

template <typename D>
struct derived : public base
{
public:
  std::shared_ptr<base> getNext(int x)
  { 
    std::shared_ptr <derived> clone = std::make_shared <derived<d>> (x, *this);
    return std::static_pointer_cast <base> (clone);
  }
};

OTHER TIPS

You can use virtual accessor-functions in the base class which tells which type the derived classes are. Then you can use static cast safely to cast the base class.

class Animal {
    virtual bool isDog() { return false; }
    virtual bool isCat() { return false; }
};

class Dog : public Animal {
    virtual bool isDog() { return true; }
};

class Cat : public Animal {
    virtual bool isCat() { return true; }
};

I can then cast the base class safely like this:

Dog A;
Cat B;

Animal *unknown_animal = &A;

if (unknown_animal->isDog()) {
    Dog *dog = static_cast<Dog*>(unknown_animal);
}
else if (unknown_animal->isCat()) {
    Cat *cat = static_cast<Cat*>(unknown_animal);
}

The accessor functions are also useful if you only need to know the derived type, but don't need to access it.

You would not want to use static_cast for this anyway; you'd want to use dynamic_pointer_cast (and check the result to make sure it was castable to your derived type).

If your interface has all the information you need to fill in your Data object, you can create a conversion constructor that will allow you to avoid doing the cast:

template<typename T>
class MyDerived : public Base
{
public:
    // other functions
    MyDerived(std::shared_ptr<Base> p)
    {
        // initialize _data
    }

private:
    Data _data;
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top