0

I'm trying to find my way around. I have two classes, A and B, where B inherits from A.

There are also two overloaded functions for A and B, that act on them in two different ways.

Now call these functions from a class function like this:

#include <iostream>

struct A;
struct B;

void f(A a);
void f(B b);

struct A {
  int i;
  A(): i(0) {};
  void thisf() { f(*this); }
};

struct B: public A {
  int j;
  B(): j(1) {};
  void thisf() { f(*this); }
};

void f(A a) { std::cout << a.i << std::endl; }
void f(B b) { std::cout << b.i << " " << b.j << std::endl; }

int main() {
  A a;
  B b;

  a.thisf();
  b.thisf();

  return 0;
}

My question is: since A::thisf() and B::thisf() are the same (and will stay the same for more subclasses), is there a way how I can omit B::thisf() while still having the same functionality?

The expected output should look like:

0
0 1

More detail why I try to do this: I want to provide some custom render functionality to different kinds of data without bloating the class with render logic and have a separate render class with some state variables. But I don't want to give up the possibility to write

 b.render()

in some situations. In my class definitions I want to spare every line I can.

I got the idea in this thread: https://gamedev.stackexchange.com/questions/63912/visitor-pattern-vs-inheritance-for-rendering

Community
  • 1
  • 1
dawirstejeck
  • 470
  • 1
  • 5
  • 12

2 Answers2

1

In your proposed problem you claim the need for a method in A and B that is not polymorphic and yet exhibits different behaviour in the two classes (linked by inheritance) with the same signature.

In addition, the method defers to a free function found by ADL (good!).

So... my question to you is this. If you already have the guarantee of a free function called f(A|B), why not simply document that as the interface?

If you insist on having thisf() you are causing yourself a problem because the inheritance relationship will cause B's thisf() to be ambiguous with A's. This can be solved with polymorphism (as per the other answer) but since you're rejecting that, you are are left with few options other than to eliminate the logically redundant thisf() altogether.

in any case, this code will do exactly as you want:

#include <iostream>

struct A;
struct B;

void f(A a);
void f(B b);

struct common_interface {
    virtual void thisf() = 0;
};

template<class Host, class Base>
struct common_interface_impl : Base {
    virtual void thisf() {
        f(static_cast<Host&>(*this));
    }
};

struct A : common_interface_impl<A, common_interface>
{
    int i;
    A(): i(0) {};
};

struct B: common_interface_impl <B, A>
{
    int j;
    B(): j(1) {};
};

void f(A a) { std::cout << a.i << std::endl; }
void f(B b) { std::cout << b.i << " " << b.j << std::endl; }

int main() {
    A a;
    B b;

    a.thisf();
    b.thisf();

    return 0;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • This is what I was looking for! But what do you mean by "document that [`f(A|B)`] as the interface"? – dawirstejeck Jul 04 '15 at 22:14
  • `A::thisf()` is merely a proxy for `f(A)`. `B::thisf()` is a proxy for `f(B)`. Therefore if you know the type of A or B at the call site, it's sufficient to call `f()`. You only need a polymorphic `thisf()` if you don't know the actual type when the call takes place. – Richard Hodges Jul 04 '15 at 22:39
-1
#include <iostream>

struct A {
  int i;
  A(): i(0) {};
  virtual ~A() {};
  virtual void thisf() { std::cout << i << std::endl; }
};

struct B: public A {
  int j;
  B(): j(1) {};
  void thisf() { A::thisf(); std::cout << j << std::endl; }
};

void f( A* a )
{
   a->thisf();
}

int main() {
  A* a = new A();
  A* b = new B();

  f( a ); f( b );

  delete a; delete b;

  return 0;
}
Robert Wadowski
  • 1,407
  • 1
  • 11
  • 14
  • Thank you for the answer but this is not exactly what I am looking for: I try to achieve that the call of f() detects for every call "intelligently" if it gets a class A or B, do you know what I mean? But I get the feeling that this isn't possible like I hoped... – dawirstejeck Jul 04 '15 at 19:42
  • This is closest thing you want – Robert Wadowski Jul 04 '15 at 19:48
  • Why the dynamic allocation all of a sudden? And what does the "virtual" do? And why the destructor for A? – dawirstejeck Jul 04 '15 at 19:50
  • this is called polimorphysm, depending on type under pointer apropriate method is called. virtual keyword creates vtable ( or called some like that ) table that holds all method with the same signature. ingheritting classes can reimplement functions. During invocation of method for pointer appropriate method will be called depending on type under pointer. More info https://en.wikipedia.org/wiki/Virtual_method_table – Robert Wadowski Jul 04 '15 at 19:54
  • Virtual destructor http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors – Robert Wadowski Jul 04 '15 at 20:04