Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I force a compiler error if an un-overridden virtual method is called?

This is a rather general question about style and safety when writing a template base class in C++. Bear with me, though, there is a specific question at the end...

I have a template base class which fully implements the required functionality when the type T is a primitive type (int, float, etc.). However,if T is not primitive (for example, if T is a class that needs to have a constructor called with a specific set of arguments) then some of the methods will need to be overridden, and for this reason they are declared virtual in the template. The template also contains a pure virtual method which forces it to be abstract, so in order to be used it must be derived from: a derived class based on a primitive type can use all the methods as provided and override just the pure virtual one, whilst a derived class based on a non-primitive type should override all the virtual methods.

For example:

template <typename T> class myTemplate
{
public:
    // Constructor:
    myTemplate<T>();

    // Destructor:
    virtual ~myTemplate();

    // General function, valid for all classes derived from this:
    void printMe()
    {
        printf("Hello, I'm from the template\r\n");
    }

    // Primitive function, must be overridden for non-primitive types:
    virtual T DoSomething(const T& myClass)
    {
        T result = result + static_cast<T>(1);
        return result;
    }

    // Primitive function, must be overridden for non-primitive types:
    virtual T DoSomethingElse(const T& myClass)
    {
        T result = result - static_cast<T>(1);
        return result;
    }

    // Pure Virtual function, must be overridden in all cases:
    virtual void RefrainFromDoingAnythingAtAll(const T& myClass) = 0
};


class myIntegerClass : public myTemplate<int>
{
public:
    virtual void RefrainFromDoingAnythingAtAll(const int& myInteger) {} 
};

Suppose now I want to create a derived class where I expect 'DoSomething' to be called, but can't envisage any circumstances under which 'DoSomethingElse' would be useful. Therefore I'd like to reimplement 'DoSomething', but not bother with 'DoSomethingElse'. Nevertheless, if at some point the derived class's 'DoSomethingElse' method does get called (either because I really meant to call 'DoSomething' but wrote the wrong thing by mistake, or because a circumstance arose which I had earlier failed to envisage), then I want a compiler warning to be issued to remind me that I can't do this unless I reimplement 'DoSomethingElse' first.

For example:

class myWatermelonClass : public myTemplate<Watermelon>
{
public:
    virtual Watermelon DoSomething(const Watermelon &myWatermelon)
    {
          // Just return a copy of the original:
          Watermelon anotherWatermelon(myWatermelon);
          return anotherWatermelon;
    }

    virtual Watermelon DoSomethingElse(const Watermelon &myWatermelon)
    {
          // This routine shouldn't ever get called: if it does, then 
          // either I've made an error or, if I find that I really need
          // to reimplement it after all, then I'd better buckle down
          // and do it.
          < How do I make the compiler remind me of this? >
    }        

    virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {}   
};

Obviously I know about the standard #error and #warning compiler directives, but if I use one of these then the error (or warning) is flagged every time I compile. What I want is to make sure that an error is given at compile time if I negligently call

DoSomethingElse(aSpecificWatermelon);

from somewhere in my code, but not otherwise. Is there a way to achieve this? Or is this just a fundamentally bad design in the first place?

like image 757
Eos Pengwern Avatar asked Jul 07 '14 08:07

Eos Pengwern


People also ask

What if virtual function is not overridden?

In addition, if you do not override a virtual member function in a derived class, a call to that function uses the function implementation defined in the base class. A function that has a deleted definition cannot override a function that does not have a deleted definition.

Is it possible to avoid a virtual method from getting overridden in C++?

In C++, there's no way to forbid it, it's just that by definition of "override", only virtual functions can be "overridden".

Can you override a virtual method?

A virtual method can be created in the base class by using the “virtual” keyword and the same method can be overridden in the derived class by using the “override” keyword.

What happens when you call a virtual function?

A call to a virtual function is resolved according to the underlying type of object for which it is called. A call to a nonvirtual function is resolved according to the type of the pointer or reference.


2 Answers

I think you are misusing templates and virtual dispatch mix. For instance, you base class is not going to compile for any type that does not support addition operator and being constructible from an int. Every time myTemplate gets specialised implicitly, all the virtual functions will be compiled, even if you override them in derived classes:

virtual T DoSomething(const T& myClass)
{
    T result = result + static_cast<T>(1); // compile error when T == Watermelon
    return result;
}

In you case, what you are looking for is the explicit template specialisation:

template <>
class myTemplate<Watermelon>
{
public:
    // General function, valid for all classes derived from this:
    void printMe()
    {
        printf("Hello, I'm from the Watermelon template specialisation\r\n");
    }

    virtual Watermelon DoSomething(const Watermelon &myWatermelon)
    {
        // Just return a copy of the original:
        Watermelon anotherWatermelon(myWatermelon);
        return anotherWatermelon;
    }

    // notice there's no DoSomethingElse!

    virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {}   
};

Now calling DoSomethingElse on an instance of myTemplate<Watermelon> will immediately give you a compile error, since there's no such function.

like image 94
gwiazdorrr Avatar answered Sep 17 '22 05:09

gwiazdorrr


By using static_assert, you can cause a compile error when you try to call a function that doesn't meet certain criteria. There is no need for virtual functions at all. Here is an example:

#include <iostream>
#include <type_traits>

using std::cout;

template <typename T>
struct myTemplate {
    void printMe() { cout << "Hello, I'm from the template\r\n"; }

    T DoSomething(const T& myClass)
    {
        static_assert(
          std::is_fundamental<T>::value,
          "DoSomething must be redefined in derived classes "
          "for non-fundamental types."
        );
        T result = myClass + static_cast<T>(1);
        return result;
    }

    T DoSomethingElse(const T& myClass)
    {
        static_assert(
          std::is_fundamental<T>::value,
          "DoSomethingElse must be redefined in derived classes "
          "for non-fundamental types."
        );
        T result = myClass - static_cast<T>(1);
        return result;
    }

    template <typename U>
    struct never_true { static const bool value = false; };

    void RefrainFromDoingAnythingAtAll(const T&)
    {
        static_assert(
            never_true<T>::value,
            "RefrainFromDoingAnythingAtAll must be redefined "
            "in derived classes."
        );
    }
};

struct Watermelon {
};

struct Lemon {
};

struct myIntegerClass : myTemplate<int> {
  void RefrainFromDoingAnythingAtAll(const int &) { }
};

struct myWatermelonClass : myTemplate<Watermelon> {
    Watermelon DoSomething(const Watermelon&)
    {
      return Watermelon();
    }

    Watermelon DoSomethingElse(const Watermelon&)
    {
      return Watermelon();
    }

    void RefrainFromDoingAnythingAtAll(const Watermelon &) { }
};


struct myLemonClass : myTemplate<Lemon> {
};

int main()
{
    myIntegerClass x;
    x.DoSomething(5); // works
    x.DoSomethingElse(5); // works
    x.RefrainFromDoingAnythingAtAll(5); // works
    myWatermelonClass y;
    y.DoSomething(Watermelon()); // works
    y.DoSomethingElse(Watermelon()); // works
    y.RefrainFromDoingAnythingAtAll(Watermelon()); // works
    myLemonClass z;
    z.DoSomething(Lemon()); // error
    z.DoSomethingElse(Lemon()); // error
    z.RefrainFromDoingAnythingAtAll(Lemon()); // error
}
like image 43
Vaughn Cato Avatar answered Sep 17 '22 05:09

Vaughn Cato