C++ override private pure virtual method as public
Asked Answered
M

5

22

Why does this happen?

http://coliru.stacked-crooked.com/a/e1376beff0c157a1

class Base{
private:
    virtual void do_run() = 0;
public:
    void run(){
        do_run();
    }
};

class A : public Base {
public:
    // uplift ??
    virtual void do_run() override {}
};


int main()
{
    A a;
    a.do_run();
}

Why can I override a PRIVATE virtual method as public?

Mountaineer answered 6/6, 2018 at 14:23 Comment(13)
because it's ok for things to call it on an A, but not on a Base?Melodramatize
@Melodramatize ? What?Mountaineer
virtual and override simply do not pay any attention to access modifiers. Notably, it's not part of the function signature, which is what virtual and override look at.Kimon
@FrançoisAndrieux Isn't the question why this is so?Manno
@FrançoisAndrieux emm... source please?Mountaineer
Because that's the nature of private? Subclasses cannot access (therefore cannot override) private methods from their parent class. I suggest you go back and read about private inheritanceAffiliate
@Affiliate But they can, that's the point of this question.Kimon
@tower120: UKMonkey means that Base& base = a; base.do_run(); would produce error.Beamends
THis is a very good question. IMO this is an error in the language design contradicting "private members cannot be seen by subclasses".Tersina
Not exactly a duplicate, but very close to it, and with explanatory answers: Private virtual method in C++Orr
@Tersina Taking a solid example; assume some logic in run that makes sure that it can't be run twice. In my specific implementation, that logic isn't required because it's done in do_run (because of some hardware feature - who knows), which makes them identical; I can safely call do_run or run; but I know that do_run will be just that little bit faster because it doesn't need to go through into the checking of run(). Well, why shouldn't I let things that KNOW they're using this implementation use the faster version; while still allowing things that don't to still work?Melodramatize
@Melodramatize I don't understand how your example justifies that it is reasonable for me as a base class author to allow a subclass author to override my method but not to call it, as this is what a private virtual function allows and disallows you to do. Could you clarify your point?Tersina
@Tersina -- "private members cannot be seen by subclasses" is simply wrong. They cannot be accessed; their names are visible.Stamm
H
23

According to https://en.cppreference.com/w/cpp/language/virtual#In_detail overriding a base's virtual member function only care about the function name, parameters, const/volatile-ness and ref qualifier. It doesn't care about return type, access modifier or other things you might expect it to care about.

The linked reference also specifically notes that :

Base::vf does not need to be visible (can be declared private, or inherited using private inheritance) to be overridden.

Nothing that I can find explicitly gives permission to do this, but the rules of overriding do not prevent it. It's allowed by virtue of virtual functions and function overriding existing and not disallowing this case.

If you are asking why this is how the language is, you may have to ask the standardization committee.

Horace answered 6/6, 2018 at 14:35 Comment(18)
Ok... How en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface should work then?Mountaineer
@Mountaineer I don't understand what you are asking. That example works because private member functions can be overridden.Kimon
I mean, its work - but if you can "accidentally" override it as public, what's the point of NVI?Mountaineer
If you accidentally override the as public, you've misused the pattern. I'm not sure how you can modify the pattern to mitigate that risk. But the same is true of countless features and technologies in c++. It's possible to make a mistake using it. This is one of the main reasons why bugs exist. Though in this case, making it public only exposes you to further problems, it doesn't cause any problems on it's own.Kimon
@Mountaineer when you declared the method as virtual and especially as pure virtual, you have to implement it. If you are asking for it, it cannot be accidentalDuteous
@KillzoneKid yes - but implement it as PUBLIC can easily be done accidental.Mountaineer
@Mountaineer you can just as well "accidentally" override PUBLIC method as private, what's then?Duteous
@KillzoneKid then I can call it. This will be interface violation. I will be able to call intentionally hided function.Mountaineer
@Mountaineer And if you make mistake other way round and make public private, you cannot call what you should be able to call publicly. Who is to decide what is worse? Was a good question neverthelessDuteous
@FrançoisAndrieux "Base::vf does not need to be visible (can be declared private, or inherited using private inheritance) to be overridden." - But from the other side, this does not mean that access level is allowed to be changed....Mountaineer
@Mountaineer The point is that the derived type's override is seen as a match for the base type's virtual member, regardless of their access modifier. Nothing in the language gives explicit permission to do this, but the rules of overriding do not prevent it. It's allowed by virtue of overriding existing and not disallowing this case. It sounds like you're already convinced that this isn't allowed and are looking for someone to validate that all of the documentation is wrong. Note that you aren't changing any access modifiers. You're supplying a new function with different a different access.Kimon
@FrançoisAndrieux IOW this allowed, because not denied. Please, update answer with this.Mountaineer
It does care about the return type though. What you probably want to say is that if they are not the same they have to be covariant.Kingsbury
@Kingsbury That's a different concern. The return type isn't considered when determining whether a member function overrides or hides base type's member function. That the return types need to be covariant is a requirement once the function has been determined to be an override. For example, if you change any of the four elements I mention, the function will be considered as hiding the base type's function. Generally this will still compile (barring the use of the override keyword). If you change the return type, you have an ill-formed function override and the compiler will complain.Kimon
@FrançoisAndrieux Ahh, it's because functions can't be overloaded using just the return type, so there is no way to hide the base function, right?Kingsbury
@Kingsbury I hadn't thought of why, but that makes sense.Kimon
@FrançoisAndrieux Your avatar is a strangely good match for this answer. Well, given your activity in C++ tag, maybe not only this one...Smasher
"overwriting a base's virtual" overwriting or overriding?Willem
S
12

That behavior is intended. If a method is virtual then it's meant to be customizable by derived classes, regardless of access modifier.

See here

Saragossa answered 6/6, 2018 at 14:30 Comment(1)
It kind of breaks the private-ness of private inheritance.Willem
B
4

Why I can override PRIVATE virtual method as public???

Because you look at the base method being private at wrong angle. B::do_run being private means "only members and friends of this class can use it". To prohibit derived classes from overriding it we would need separate specifier but we can simply make it not virtual. Class A on another side allows anybody to call A::do_run() and it is up to class A designer to decide so. So there is no uplift as you see it.

Brittaniebrittany answered 6/6, 2018 at 14:52 Comment(5)
Well, there is an uplift: A is neither member nor friend, so I would expect it to have no access to private function of B whatsoever. This is incredibly inconsistent.Smasher
@Smasher A has no access to a private function of Base. It cannot place a call to Base::do_run. It can customize a customization point that Base offered, but that doesn't involve any form of access.Writhen
@Writhen The validity of the declaration overriding the base class declaration is checked by comparing to the base class, inaccessible, declaration. Saying that it isn't an "access" really is a stretch.Willem
@Willem Are you inventing a new meaning for the word "access"? Its technical meaning (to which "private:" relates) applies when compiling a function call.Writhen
@Writhen I wasn't aware of a technical meaning different from the common sense meaning. Your code depends on a declaration. Compilation must access that declaration. Common sense. That's all.Willem
W
2

Notice that this implementation does not change the way how the base class is accessed and a construct:

Base& b = a;
b.do_run();

will not work.

I remember there is some rationale behind it described in more detail in "Effective C++" by Scott Meyers. But the key practical feature is to be able to use such flexibility in the opposite direction, to override public base class members with private functions in a derived class forcing the client to use the base class as the interface and not be tempted to use directly the derived one that should remain a hidden implementation.

Worm answered 6/6, 2018 at 15:26 Comment(3)
Yes, overriding, implementing, but not changing visibility.Mountaineer
@Mountaineer You may change the visibility of members in a derived class but regardless of this the base class interface will always work following its own definition. Whatever happens in a derived (implementing) class is expected to be an implementation detail and be less visible and not affecting the base interface. And with providing implementation some extra flexibility may be sometimes handy.Worm
@Worm Access control (public, protected, private) is not the same as visibility.Willem
R
0

If the intention is to write a private code for the base-class & to prevent the possibility of override it, implement the private function in the base class, and declare it final, otherwise: When should someone use private virtuals? ISOCPP.ORG FAQ

Ricardaricardama answered 29/6, 2018 at 2:2 Comment(5)
If the function should not be overridden, just don't make it virtual. (Am I missing something?)Willem
Let's say you have a base-class B with (no virtual) void f1(int); and a derive-class D with void f1(int); - bad (hope for a warning): D::f1() hides B::f1(). Visual Studio C++ Compiler warnings C26434 on Code-Analysis. On the other hand: if you have base-class B with virtual void f1(int) final; and derived-class D with void f1(int); - you get an ERROR: overriding final function.Ricardaricardama
I see but it the effort worth it? Also, making a class polymorphic as a (small) cost.Willem
Overall, name hiding is dangerous and error-prone. In some cases, as a strong design-statement, I assume it worth the "effort".Ricardaricardama
But compilers often can warn about name hiding without forcing the programmer to make member function virtual final. Also the method can only work for non static member functions, so this is no fix for cases of hiding of a static member, a data member, a type definition member, or a non static template function.Willem

© 2022 - 2024 — McMap. All rights reserved.