Search code examples
c++inheritanceoverridingvirtual-functionspure-virtual

Force a derived class to override one of a set of virtual functions


Given a base class which has some virtual functions, can anyone think of a way to force a derived class to override exactly one of a set of virtual functions, at compile time? Or an alternative formulation of a class hierarchy that achieves the same thing?

In code:

struct Base
{
    // Some imaginary syntax to indicate the following are a "pure override set"
    // [
    virtual void function1(int) = 0;
    virtual void function2(float) = 0;
    // ...
    // ]
};

struct Derived1 : Base {}; // ERROR not implemented
struct Derived2 : Base { void function1(int) override; }; // OK
struct Derived3 : Base { void function2(float) override; }; // OK

struct Derived4 : Base // ERROR too many implemented
{
    void function1(int) override;
    void function2(float) override;
};

I'm not sure I really have an actual use case for this, but it occurred to me as I was implementing something that loosely follows this pattern and thought it was an interesting question to ponder, if nothing else.


Solution

  • No, but you can fake it.

    Base has non-virtual float and int methods that forward to a pure virtual std variant one.

    Two helper classes, one int one float, implement the std variant one, forwarding both cases to either a pure virtual int or float implementation.

    It is in charge of dealing with the 'wrong type' case.

    Derived inherit from one or another helper, and implement only int or float.

    struct Base
    {
        void function1(int x) { vfunction(x); }
        void function2(float x) { vfunction(x); }
        virtual void vfunction(std::variant<int,float>) = 0;
    };
    struct Helper1:Base {
        void vfunction(std::variant<int,float> v) final {
          if (std::holds_alternative<int>(v))
            function1_impl( std::get<int>(v) );
        }
        virtual void function1_impl(int x) = 0;
    };
    struct Helper2:Base {
        void vfunction(std::variant<int,float> v) final {
          if (std::holds_alternative<float>(v))
            function2_impl( std::get<float>(v) );
        }
        virtual void function2_impl(float x) = 0;
    };
    
    struct Derived1 : Base {}; // ERROR not implemented
    struct Derived2 : Helper1 { void function1_impl(int) override; }; // OK
    struct Derived3 : Helper2 { void function2_impl(float) override; }; // OK
    

    This uses https://en.wikipedia.org/wiki/Non-virtual_interface_pattern -- the interface contains non-virtual methods, whose details can be overridden to make them behave differently.

    If you are afraid people will override vfunction you can use the private lock technique, and/or just give it a name like private_implementation_detail_do_not_implement and trust your code review process.