sdeveloper
sdeveloper

Reputation: 149

Overriding typedefs in C++

I have a generic struct

template<typename X, typename Y>
struct S
{

};

Then there is an abstract class which returns shared pointer to above struct in a pure virtual function. One of the derived classes also implements this function but needs to return pointer to S with different instantiated type:

typedef boost::shared_pointer<S<double, double>> BPtr;
class BaseClass
{
  public:
    BPtr Func() = 0;
};

typedef boost::shared_pointer<S<double, std::tuple<double, double>>>    Dptr;
class DerivedClass : public BaseClass
{
   public:
      DPtr Func() {}
}

Ideally I would like to be able to return an override typedef in the derived class but can't do that in C++.

How do I fix this dilemma?

Upvotes: 3

Views: 6079

Answers (4)

Pixelchemist
Pixelchemist

Reputation: 24946

An (abstract) base class with virtual functions is used to provide the interface for the derived classes. You cannot have the same function return different (more precisily non-covariant) things depending on the derived type because the user has a contract with the base class.

If I have a

Base * p = /* get whatever derived instance here */;

it might very well be the case that I cannot even tell what actual derived type is hidden behind the pointer.

Therefore, if Func is declared as

boost::shared_ptr<S<double, double>> Func();

I'd expect it to return boost::shared_ptr<S<double, double>> and not something that depends on the actual derived type.

The reason is simple: If some derived type would be added that returns something that is incompatible with my code it would break my implementation without the interface being changed at all.

It would work however, if S<double, std::tuple<double, double>> would derive from S<double, double>.

You cannot get around that restriction with typedefs because they are just alias names for the types they refer to. With a typedef like using BPtr = boost::shared_ptr<S<double, double>>;, this declaration:

BPtr foo();

is equivalent to

boost::shared_ptr<S<double, double>> foo();

it is just less typing involved if the typedef is used repeatedly.

Upvotes: 0

R Sahu
R Sahu

Reputation: 206627

First of all, I think you probably meant to make Base::Func() a virtual member function.

class BaseClass
{
  public:
    virtual BPtr Func() = 0;
 // ^^^ Missing 
};

Re:

Ideally I would like to be able to return an override typedef in the derived class but can't do that in C++.

You can override the typedef in the derived class but that does not necessarily make it a valid return type of the virtual member function.

For instance, if I have:

struct Foo
{
   typedef int return_type;

   virtual return_type func() = 0;
};

struct Bar : Foo
{
   typedef double return_type;

   virtual return_type func() override { return 1.0; }
};

You should get a compiler error. Just because I use return_type in both the base class and the derived class does not make them suitable to be used as the return type of the virtual member function.

The return type in an overridden implementation can be covariant type, not any old type. A typedef is just an alias. It's not a true type.

Re:

How do I fix this dilemma?

You'll have to rethink your return types. You cannot use boost::shared_pointer<S<double, std::tuple<double, double>>> for the return type in the derived class when the return type in the base class is boost::shared_pointer<S<double, double>>.

Upvotes: 2

Thomas B Preusser
Thomas B Preusser

Reputation: 1189

Whatever DerivedClass::Func returns must be a subtype of what BaseClass::Func returns. Otherwise, DerivedClass is obviously not a specialization of BaseClass.

What makes you build this inheritance relationship in the first place? Without it, you could just make the return type of Func a template parameter.

Upvotes: 2

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17415

Firstly, the smart pointers: They don't have a common baseclass (just as vector<baseclass> is not a baseclass of vector<derived>), so covariant return types won't work.

Secondly, even if you replaced the smart pointers with raw pointers, different instantiations of a class template don't share a common base either (like vector and smart pointers), so you're out of luck.

Note that different instantiations of class templates can actually derive from one another, so if S<double,double> was a baseclass for S<double,tuple<double,double>>, you could use covariant returns, but that seems like a wonky approach.

In summary, I think you should describe why you think that an S<T,tuple<T,T>> could be treated like an S<T,T>, which seems the underlying assumption in your code.

Upvotes: 4

Related Questions