Tim Angus
Tim Angus

Reputation: 1113

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.

Upvotes: 2

Views: 435

Answers (3)

R Sahu
R Sahu

Reputation: 206567

Or an alternative formulation of a class hierarchy that achieves the same thing?

One option is to have an intermediate base class that implements one function.

struct Base
{
    virtual ~Base() {};
    virtual void function(int) = 0;
    virtual void function(float) = 0;
};

template <typename T>
struct TBase : Base
{
   virtual void function(T) override {} 
};

struct Derived1 : Base {};
struct Derived2 : TBase<float> { void function(int) override {} };
struct Derived3 : TBase<int> { void function(float) override {} };

int main()
{
   Derived1 d1; // ERROR. Virtual functions are not implemented
   Derived2 d2; // OK.
   Derived3 d3; // OK.
}

Note that the functions are named function in this approach, not function1 and function2.

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275260

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.

Upvotes: 4

Joseph Larson
Joseph Larson

Reputation: 9058

Your classes will remain abstract if you don't override all the abstract virtual methods. You have to do all of them if you want to instantiate the object.

Upvotes: 0

Related Questions