svoltron
svoltron

Reputation: 365

Inheritance and casting with templates not working as expected

I've been playing around with template and inheritance but there is something strange about using virtual function members with template parameters when performing a cast to the base class. They seem to work using "direct inheritance" but not if I "defer" the inheritance later on.

A bit of code to illustrate:

Example [1]

struct CastExBase
  {
  virtual void f() {}
  };

template<class RT>
struct CastExA : CastExBase
  {
  void f() {std::cout << "CastExA" << std::endl;}
  virtual void g() {std::cout << "g - A" << std::endl;}
  virtual RT h() {std::cout << "h - A" << std::endl;}
  };

struct CastExB
  {
  void execF() {std::cout << "CastExB" << std::endl;}
  void g() {std::cout << "g - B" << std::endl;}
  int h() {std::cout << "h - B" << std::endl;}
  };

struct CastExC :
    public CastExA<int>,
    protected CastExB
  {
  void f() override
    {
    (static_cast<CastExB*>(this))->execF();
    }

  void g() override
    {
    (static_cast<CastExB*>(this))->g();
    }
  };

Test case:

  CastExBase* a2 = new CastExC();
  CastExA<int>* a3 = (CastExA<int>*) a2;
  a3->g(); // This prints g - B as expected
  a3->h(); // This prints h - A ... why???

Why a3->h() does not print h - B?

I tried also another test directly inheriting from the base class and in this case it correctly works.

Example [2]

struct CastExDBase
      {
      };

    template<class T>
    struct CastExD : CastExDBase
      {
      virtual T f() {std::cout << "CastExD" << std::endl;}
      };

    struct CastExE : CastExD<int>
      {
      int f() {std::cout << "CastExE" << std::endl;}
      };

Test case:

  CastExDBase* d1 = new CastExE();
  CastExD<int>* d2 = (CastExD<int>*) d1;
  d2->f(); // This prints CastExE as expected

Is this related to UB?

Upvotes: 4

Views: 133

Answers (2)

Marek Jamiołkowski
Marek Jamiołkowski

Reputation: 41

There is no h() in CastExC. Maybe that's why.

Note: CastExA and CastExB are not related.

Upvotes: 4

Holt
Holt

Reputation: 37606

Even if CastExC inherits both CastExA<int> and CstExB, the definition of h() in CstExB will not override the "definition" of h() in CastExA<int> because CastExA<int> and CstExB are not related. If you try to do this:

CastExBase* a2 = new CastExC();
CastExC* a3 = (CastExC*) a2;
a3->h();

You will get an ambiguous request for h(). If you want to overload, you need to do this manually:

struct CastExC: protected CastExB, public CastExA<int> {
    virtual int h() override { return CastExB::h(); }
};

Upvotes: 5

Related Questions