Force all classes to implement / override a 'pure virtual' method in multi-level inheritance hierarchy
I found one mechanism, where at least we are prompted to announce the overridden method explicitly. It's not the perfect way though.
Suppose, we have few pure virtual
methods in the base class B
:
class B {
virtual void foo () = 0;
virtual void bar (int) = 0;
};
Among them, suppose we want only foo()
to be overridden by the whole hierarchy. For simplicity, we have to have a virtual
base class, which contains that particular method. It has a template constructor, which just accepts the type same as that method.
class Register_foo {
virtual void foo () = 0; // declare here
template<typename T> // this matches the signature of 'foo'
Register_foo (void (T::*)()) {}
};
class B : public virtual Register_foo { // <---- virtual inheritance
virtual void bar (int) = 0;
Base () : Register_foo(&Base::foo) {} // <--- explicitly pass the function name
};
Every subsequent child class in the hierarchy would have to register a foo
inside its every constructor explicitly. e.g.:
struct D : B {
D () : Register_foo(&D::foo) {}
virtual void foo () {};
};
This registration mechanism has nothing to do with the business logic. Though, the child class
can choose to register using its own foo
or its parent's foo
or even some similar syntax method, but at least that is announced explicitly.
In a multi-level inheritance, does a grandchild require to implement a pure virtual method, if its parent has already implemented it?
Parent's start()
is enough.
You should though use the override keyword for B's reimplementation :
class B : public A {
public: void start() override;
};
It makes it clear that the method is an implementation of a virtual one and the compiler will enforce that it will always be the case, even with future changes on class A.
How to force all derived classes to implement a virtual method?
Using curiously recurring template fun, you can achieve something quite similar:
template<typename T>
class Cloneable : public T, public Dep
{
private:
Cloneable<T>() : T() { }
public:
static Cloneable<T>* Create() { return new Cloneable<T>(); }
Cloneable<T>* clone() { return new Cloneable<T>(*this); }
};
Instead of deriving from Dep
and instantiating via new MyType
, use Cloneable<MyType>::Create
. Since Cloneable<MyType>
is derived from MyType
, you can use the instance the same way you would use any MyType
, except that it is now guaranteed to have Dep::clone
.
Additionally your Master
should not accept an instance of type Dep
, but enforce that it is a Cloneable<T>
. (Replace your orignial function by a simple function template that enforces this.) This guarantees that any Dep
inside the master has a correctly implemented clone
function.
Since Cloneable<MyType>
has no public constructor, it cannot be inherited, however your actual MyType
can be further inherited and used just as before.
Multilevel Inheritance and Pure Virtual Functions
class Grand_Son:public Son {
public:
---^^^^^^^
void foo() {
cout<<"\nFunction foo From Grand_Son\n";
}
};
You haven't declared foo
public - I added it above.
Force a derived class to override one of a set of virtual functions
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.
Turning a non-pure virtual function into pure in a subclass
You're fine if implemented as you present in your explanation. Without going into entire sections on abstract base classes and virtual functions, the standard dictates:
C++ § 10.4p2
An abstract class is a class that can be used only as a base class of some other class; no objects of an abstract class can be created except as subobjects of a class derived from it. A class is abstract if it has at least one pure virtual function. [ Note: Such a function might be inherited: see below. — end note ] A virtual function is specified pure by using a pure-specifier (9.2) in the function declaration in the class definition. A pure virtual function need be defined only if called with, or as if with (12.4), the qualified-id syntax (5.1)
The "below" referenced above leads to this note:
C++11 § 10.4p5
[Note: An abstract class can be derived from a class that is not abstract, and a pure virtual function may override a virtual function which is not pure. - end note]
Require override of parent virtual function
There's no easy way of doing so in C++.
There is a hack though, explained in this answer, using virtual inheritance and forcing your classes to register which serialize
method they are using.
Related Topics
Inheritance and Templates in C++ - Why Are Inherited Members Invisible
What's the Most Reliable Way to Prohibit a Copy Constructor in C++
Virtual Destructor and Undefined Behavior
Why Isn't C/C++'s "#Pragma Once" an Iso Standard
How to Build Cmake Externalproject While Configurating Main One
C++ Expected Constant Expression
Use Channel Hiearchy of Boost.Log for Severity and Sink Filtering
For Nested Templates, When Did '>>' Become Standard C++ (Instead of '> >')
Efficient Bitwise Operations for Counting Bits or Find the Right|Left Most Ones
Writing Directly to Std::String Internal Buffers
Is There a Compiler Bug Exposed by My Implementation of an Is_Complete Type Trait
Is This Undefined Behavior with Const_Cast
Insert VS Emplace VS Operator[] in C++ Map