4

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?

svoltron
  • 365
  • 1
  • 10
  • 2
    `CastExA* a3 = (CastExA*) a2;` - You cannot downcast with a C-style cast, you need to `dynamic_cast` here. – Holt Jan 16 '19 at 08:42
  • ideally yes but I know that the type will be CastExA* so should be ok to use it. Anyway, I tried also to dynamic cast it but same result – svoltron Jan 16 '19 at 08:47
  • 2
    General tip: Never do C-style casts in C++. If you ever need to do it it's usually a red flag that you're doing something wrong. – Some programmer dude Jan 16 '19 at 08:47
  • Yep, I should have used static_cast, I agree – svoltron Jan 16 '19 at 08:50
  • 1
    Closely related, possibly dupe: https://stackoverflow.com/questions/19528338/how-to-override-a-function-in-another-base-class – Holt Jan 16 '19 at 08:59

2 Answers2

5

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(); }
};
Holt
  • 36,600
  • 7
  • 92
  • 139
4

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

Note: CastExA and CastExB are not related.

ETO
  • 6,970
  • 1
  • 20
  • 37