Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 419
svoltron Avatar asked Jan 02 '23 10:01

svoltron


1 Answers

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(); }
};
like image 84
Holt Avatar answered Jan 03 '23 22:01

Holt