Imagine I have some NVI interface that allows for customization points via virtual functions.
And those virtual functions are designated solely for customization within the NVI and should never be invocable outside its context (explicitly called by derived classes elsewhere).
Making those functions private disallows the inheriting classes to call it explicitly (which is intended). But it also prevents the class from calling a default implementation (which is not intended).
I know I can write a comment to say "never call the base function outside the overriden function", but it leaves the ways to violate the interface.
Here's a small example:
class nvi
{
public:
void operator()(int i) const {
// do stuff
_customization(i);
// other stuff
}
protected:
~nvi() = default;
private:
virtual void _customization(int) const {}
};
class custom final : public nvi
{
public:
void some_unrelated_func() {
// this should not be possible
// base::_customization(815);
}
private:
void _customization(int i) const override {
// do custom stuff
// this should be possible
nvi::_customization(i);
}
};
Is it possible to make the virtual function private in the base class and still be able to call in derived, but only in the overriden?
This is an XY question, but the X is not under my control here. Personally I think that having the function protected is ok when it doesn't mutate the state or have any observable side effects, so I wouldn't care if inherited classes would call explicitly call it somewhere else...
Also, the possible solution should not be more complicated than the simple comment about the proper usage.
If you can modify
nvithis is possible. Here's the full code, with an explanation to follow:The idea is basically to just use tag-dispatch. We define a type
private_tag, which can be named or copied by any sub-class, but can only be constructed inside the scope ofnviand nowhere else. This idiom encodes "access rights" in values that we can pass around.So
operator(int)creates aprivate_tagand passes it to_customization. Then it follow that any_customizationoverride can either ignore the tag or it can pass it to some function that accepts it (such asnvi::_customizationor_customizationin any class between the final overrider andnvi).Other member functions can't call
nvi::_customization(or even their own_customization1) because they have no way to create aprivate_tag. This gives us the semantics you want more or less.1 - Not really that limiting because we can always have
_customizationdelegate to some private function that we *may* call.