1

Consider this:

struct Base {
    virtual void fn() = 0;
};

struct A: Base {
    virtual void fn();
};

struct B: A {
    // fn is not overridden here
};

Basically, fn is implemented in A. B derives from A, and B doesn't override fn.

I'd like to have technique to make B must override fn, because it is an error, if it is not overridden.

Is is possible to do this? A compile-time error (or maybe warning) would be the best, but if it is not possible, then a runtime error is OK too. I'd just like to know, if someone forgets to override fn in a derived class.

The reason for this? fn could return class-related information. For example, it could return the class name. Or it could return the amount of allocated space the object uses (for debug purposes). Or do some class-related task (for example, loading/saving its state).

geza
  • 28,403
  • 6
  • 61
  • 135
  • 1
    If `A` has a concrete implementation of `fn`, why is it unacceptable for `B` to use it? – ShadowRanger Nov 29 '17 at 02:37
  • @ShadowRanger: because `fn` does something, which should be unique for each class. For example, it could return some type-unique information. – geza Nov 29 '17 at 02:38
  • 2
    That's a terrible design choice. If `B` inherits from `A`, the default should be to act like `A` unless explicitly overridden. If `B` exists, say, solely to have a different output format, or different sort order, but otherwise behave identically to `A`, why should the implementer of `B` be forced to reimplement a function wholly unrelated to their design goal? – ShadowRanger Nov 29 '17 at 02:41
  • Can we assume you need to be able to instantiate B? – user4581301 Nov 29 '17 at 02:42
  • @user4581301: yes, both `A` and `B` can be instantiated. – geza Nov 29 '17 at 02:43
  • looks like a kindof non-virtual polymorphism ? – engf-010 Nov 29 '17 at 02:44
  • @ShadowRanger: consider, if `fn` is actually `getClassName()`, or something like this. So `fn` is not an operation on a class, but provides some meta information, or does something class-related task. And it is an error, if it is not overridden. Or other example: `getSizeOf()`, which would return some kind of size-metric. – geza Nov 29 '17 at 02:44
  • 1
    @geza: Well, if `B` is supposed to be a thin layer over `A` that even looks like `A`, then it makes sense to let it lie. There are stricter type introspection tools available, reinventing your own without compiler assistance doesn't seem like a compelling argument. – ShadowRanger Nov 29 '17 at 02:45
  • @ShadowRanger: No, it is not a thin layer. – geza Nov 29 '17 at 02:49
  • There is no direct way to do this that I can think of. Abstracting a step further and putting the mandatory function in it's own class that everyone MUST inherit won't work, it diamonds. I think you have to go into voodoo that'll probably wind up worse to maintain. – user4581301 Nov 29 '17 at 02:50
  • 1
    Someone has to have asked this before. What sayeth the google in response to "force override of function in all subclasses C++"? – user4581301 Nov 29 '17 at 02:51
  • @user4581301: yeah, I didn't find a solution for this either. A mandatory class have the same problem: one can forget it. – geza Nov 29 '17 at 02:51
  • @user4581301: I've checked this briefly. All I found is about pure virtual functions. – geza Nov 29 '17 at 02:53
  • 1
    This seems to invoke a minimum of voodoo: https://stackoverflow.com/questions/9477581/force-all-classes-to-implement-override-a-pure-virtual-method-in-multi-level Haven't tried it, though. This is one of those things that I suspect will be better managed with code review and the Mark I eyeball. – user4581301 Nov 29 '17 at 02:55
  • @user4581301: thanks for the search! That solution is ugly unfortunately. – geza Nov 29 '17 at 03:19
  • @ShadowRanger: yeah, if nothing is changed in C++17 in this regard, then it's a duplicate. – geza Nov 29 '17 at 17:14

1 Answers1

2

You cannot force the compiler to generate an error if fn is not overridden in B.

You can modify your code a little bit to get what you want.

  1. Make A::fn a pure-virtual. Leave the implementation as is in A. Remember that it's perfectly OK to implement A::fn even when it is declared pure-virtual.

  2. That will force you to override fn in B. The implementation of B::fn can utilize as much of A::fn as it needs to.


truct Base {
    virtual void fn() = 0;
};

struct A : Base {
    virtual void fn() = 0;
};

void A::fn()
{
   // Add implmentation details
}

struct B : A {
    // fn must be  overridden here
    virtual void fn();
};

void B::fn()
{
   A::fn();
   // Add additonal logic for B
}

You can generate run time error though if A::fn is called an on B object.

Here's one way to do it.

#include <iostream>

struct Base
{
   virtual void fn() = 0;

   virtual int getTypeID() = 0;

   protected:

   static int getNextID()
   {
      static int nextid = 0;
      return ++nextid;
   }

   static int getClassTypeID()
   {
      static int id = getNextID();
      return id;
   }
};

struct A : Base 
{
   virtual void fn();

   virtual int getTypeID()
   {
      return getClassTypeID();
   }

   private:

   static int getClassTypeID()
   {
      static int id = getNextID();
      return id;
   }
};

void A::fn()
{
   if ( this->getTypeID() != A::getClassTypeID()  )
   {
      // Problem.
      std::cout << "ERROR. fn() called on a derived class object.\n";
   }
   else
   {
      std::cout << "OK. fn() called on an A object.\n";
   }
}

struct B : A
{
   virtual int getTypeID()
   {
      return getClassTypeID();
   }

   static int getClassTypeID()
   {
      static int id = getNextID();
      return id;
   }
};

int main()
{
   A* a1Ptr = new A;
   A* a2Ptr = new B;

   a1Ptr->fn();
   a2Ptr->fn();
}

Output:

OK. fn() called on an A object.
ERROR. fn() called on a derived class object.
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Thanks. The problem is that `A` must be instantiatable. – geza Nov 29 '17 at 03:08
  • 1
    @geza, that indicates to me a poor choice of class hierarchies, then. Normally, you want to design your program such that only leaf level classes in a class hierarchy need to be instantiated. – R Sahu Nov 29 '17 at 03:10
  • Yes, I agree with you, that's the general case usually. But sometimes this rule is violated. There are cases, where it is artificial to follow this rule, and results in extra classes for no real reason – geza Nov 29 '17 at 03:12