hfhc2
hfhc2

Reputation: 4421

Inheritance and templates in C++

I have the following problem with inheritance and templates:

class Base {};
class Deriv : public Base {};

template <class T> class X{};

void f(X<Base>& inst) {}

int main()
{
  X<Base> xb;
  f(xb);
  X<Deriv> xd;
  f(xd);
  return 0;
}

The program doesn't compile because there is not relation between X<Base> and X<Deriv>. Nevertheless I think it should be possible to do everything that can be done with X<Base> also with X<Deriv>. Is there anything that I could do other than copying the function body of f to a new function void g(X<Deriv>& inst)?

Upvotes: 2

Views: 199

Answers (5)

Seth Carnegie
Seth Carnegie

Reputation: 75150

Why do you think they should be related? Consider the following:

template<typename T>
class X;

template<>
class X<Base> {
    int x;
};

template<>
class X<Deriv> {
    double d;
};

They're definitely not interchangeable. So no, there is no relation between those classes and you can't pass one to a function expecting the other. You'll have to do something like make both types inherit from another common type that exposes the interface you need.


Regarding your comment, you can use type traits and static_assert to do what you would do in Java:

template<typename T>
void f(X<T>& inst) {
    static_assert(std::is_base_of(Base, T)::value, "Template type must subclass Base");

    // body of function...
}

Upvotes: 1

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145429

It depends. Consider the case where X is std::shared_ptr. It would break type safety if std::shared_ptr<Derived> was derived from std::shared_ptr<Base>, but instead there is an implicit value conversion.

However, since you’re passing by reference to non-const, such a value conversion will not help you you directly.

Other possibilities include inheriting from a common interface, and templating your function.

Upvotes: 0

Different instantiations of a template are unrelated types, even if the instantiating template arguments are related. That is, X<A> is not related to X<B> regardless of what the relationship between A and B might be.

Now as of what can be done, it depends on what your template actually is. In some cases you can provide conversions so that the X<Derived> can be converted to a X<Base> for a particular operation. Another alternative is modifying your function to be able to take any X<T> for which T derives from Base (this can be done by creating a template and using SFINAE to disallow calling it with Ts that don't derive from Base. Again, depending on what your template is, you might be able to offer access to the underlying type, in which case the function could take a reference to Base (consider shared_ptr or unique_ptr with the .get() method)

Without a description of what you actually want to get done it is impossible to provide a good alternative.

Upvotes: 0

Bo Persson
Bo Persson

Reputation: 92381

You could just continue using templates:

template<class T>
void f(X<T>& inst) {}

will work for both X<Base> and X<Derived>.

The compiler might duplicate the code (if it is not smart enough), but you don't have to.

Upvotes: 2

Puppy
Puppy

Reputation: 147036

If you need such functionality, then you must template on the type- or overload, as you have said. Alternatively, you might explicitly specialize X such that X<Derived> : X<Base>.

Upvotes: 0

Related Questions