2

The same question exists for C#, but does not apply to C++.

class Base
{
    void dispatch()
    {
        if (newStyleHasBeenOverridden())   //how to find this out?
            newStyle(42);
        else
            oldStyle(1, 2);
    }

    virtual void oldStyle(int, int) { throw "Implement me!"; }
    virtual void newStyle(int) { throw "Implement me!"; }
}

class Derived:public Base
{
    void newStyle(int) override
    {
        std::cout<<"Success!";
    }
}
David R Tribble
  • 11,918
  • 5
  • 42
  • 52
Dženan
  • 3,329
  • 3
  • 31
  • 44
  • 6
    The short answer is: nope. C++ does not work this way. You will need to figure out what your real problem is, because [this is an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Sam Varshavchik May 17 '18 at 00:13
  • 1
    Maybe you can compare the pointers (addresses) of the two class member functions. But as @SamVarshavchik said, why do you need to do this? – David R Tribble May 17 '18 at 00:13
  • Related: https://stackoverflow.com/q/45180615/1896169 – Justin May 17 '18 at 00:17
  • 1
    Nope, @DavidRTribble -- you cannot "compare the pointers of the two class member functions". For starters, they are two different classes. And finally, you will discover that a pointer to a virtual class member function is the same pointer, whether the particular instance's virtual class is overridden, or not. That's how virtual functions work, after all. – Sam Varshavchik May 17 '18 at 00:17
  • @SamVarshavchik, yeah I realized this a few minutes after posting. But like you said, OP's real problem is properly using overridden member functions. – David R Tribble May 17 '18 at 00:30
  • An abstract class can assume, if any of its non-static members are being called, that a derived class has overridden all its pure virtual methods ;) – Peter May 17 '18 at 00:42
  • @SamVarshavchik comparison of pointer to virtual member function gives *unspecified* result (expr.eq/3.3) – M.M May 17 '18 at 01:14

3 Answers3

3

WARNING: This solution is not cross-platform in that it relies on a GCC extension and some undefined behavior.

GCC allows a syntax to grab the pointer to the function from the vtable of this by saying this->*&ClassName::functionName. It is probably not a good idea to actually use this, but here's a demo anyway:

#include <iostream>

class Base {
public:
    void foo() {
        auto base_bar_addr = reinterpret_cast<void*>(&Base::bar);
        auto this_bar_addr = reinterpret_cast<void*>(this->*&Base::bar);
        std::cout << (base_bar_addr == this_bar_addr ? "not overridden" : "overridden") << std::endl;
    }

    virtual void bar() { };
};

class Regular : public Base { };

class Overriding : public Base {
public:
    virtual void bar() { };
};

int main() {
    Regular r;
    r.foo();
    Overriding o;
    o.foo();
}

And for posterity:

  • ICC allows the syntax, but it has a different meaning, which is the same as just saying &Base::bar, so you'll always think it isn't being overridden.
  • Clang and MSVC reject the code outright.
Travis Gockel
  • 26,877
  • 14
  • 89
  • 116
3

This is a design problem.

However, in the interest of answering the actual question, there are a couple ways you could accomplish this without a redesign (but really, you should redesign it).

One (terrible) option is to call the newstyle method and catch the exception that occurs if it's not overridden.

void dispatch() {
    try {
        newStyle(42);
    } catch (const char *) {
        oldStyle(1, 2);
    }
}

If newStyle has been overridden, the override will be called. Otherwise, the base implementation will throw, which dispatch will catch and then fall back to oldStyle. This is an abuse of exceptions and it will perform poorly.

Another (slightly less terrible) approach is to make the base implementation of newStyle forward to oldStyle.

void dispatch() {
    newStyle(42);
}

virtual void newStyle(int) { oldStyle(1, 2); }
virtual void oldStyle(int, int) { throw "implement me"; }

This at least moves in the direction of a better design. The point of inheritance is to allow high level code to be able to use objects interchangeably, regardless of their specialization. If dispatch has to inspect the actual object type, then you've violated the Liskov Substitution Principle. Dispatch should be able to treat all the objects the same way, and any differences in behavior should arise from the overridden methods themselves (rather than the existence of overrides).

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • > One (terrible) option is to call the newstyle method and catch the exception that occurs if it's not overridden. This was my first solution which I was trying to work around by finding if the method was overloaded! – Dženan May 25 '18 at 13:37
  • > newStyle forward to oldStyle This does not work properly because the old style had access to more information i.e. the additional second parameter. – Dženan May 25 '18 at 13:38
  • 1
    Yeah, I said it was a terrible solution. Your example had the parameters hard-coded; I was just following your lead. You could have dispatch stash the extra information in member variables which the base implementation of newStyle would access to pass the values along to oldStyle. Then you get into multithreading issues ... It's a mess. – Adrian McCarthy May 25 '18 at 17:06
  • `newStyle` and `oldStyle` are invoked as thread functions, `oldStyle` having threadId parameter and the `newStyle` without it :) – Dženan May 25 '18 at 17:25
0

Making things simpler, the dispatch decision is done by the Derived class.
Abstract Base class is basically just an "interface" where the Derived class should implement all virtual functions.

The problem too sounded like an XY problem.

I thought this is what you want:

class Base // abstract class
{
   virtual void oldStyle(int, int) = 0; // pure virtual functions
   virtual void newStyle(int) = 0;      // needs to be implemented
};

class Derived:public Base
{
   public:
      Derived(bool useNewStyle): _useNewStyle(useNewStyle) {}
      void newStyle(int) { std::cout << "new style"; }
      void oldStyle(int, int) { std::cout << "old style"; }
      void dispatch()
      {
         if (_useNewStyle) {
            newStyle(42);
            return;
         }
         oldStyle(1, 2);
         return;
      }

   private:
      bool _useNewStyle = false;
};

Derived d(true); // use new style
d.dispatch();    // "new style"
Joseph D.
  • 11,804
  • 3
  • 34
  • 67