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?

cela
  • 2,352
  • 3
  • 21
  • 43
tower120
  • 5,007
  • 6
  • 40
  • 88
  • because it's ok for things to call it on an A, but not on a Base? – UKMonkey Jun 06 '18 at 14:23
  • @UKMonkey ? What? – tower120 Jun 06 '18 at 14:24
  • 6
    `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. – François Andrieux Jun 06 '18 at 14:26
  • @FrançoisAndrieux Isn't the question why this is so? – juanchopanza Jun 06 '18 at 14:26
  • @FrançoisAndrieux emm... source please? – tower120 Jun 06 '18 at 14:26
  • 1
    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 inheritance – AlexG Jun 06 '18 at 14:26
  • 10
    @AlexG But they can, that's the point of this question. – François Andrieux Jun 06 '18 at 14:27
  • 3
    @tower120: UKMonkey means that `Base& base = a; base.do_run();` would produce error. – Jarod42 Jun 06 '18 at 14:29
  • 6
    THis is a very good question. IMO this is an error in the language design contradicting "private members cannot be seen by subclasses". – sebrockm Jun 06 '18 at 14:30
  • 1
    Not exactly a duplicate, but very close to it, and with explanatory answers: [Private virtual method in C++](https://stackoverflow.com/q/2170688/1332041) – acraig5075 Jun 06 '18 at 14:44
  • @sebrockm 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? – UKMonkey Jun 06 '18 at 14:50
  • 1
    @UKMonkey 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? – sebrockm Jun 06 '18 at 15:07
  • 2
    @sebrockm -- "private members cannot be seen by subclasses" is simply wrong. They cannot be **accessed**; their names are **visible**. – Pete Becker Jun 07 '18 at 18:55

5 Answers5

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.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • Ok... How https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface should work then? – tower120 Jun 06 '18 at 14:37
  • 1
    @tower120 I don't understand what you are asking. That example works *because* `private` member functions can be overridden. – François Andrieux Jun 06 '18 at 14:40
  • I mean, its work - but if you can "accidentally" override it as public, what's the point of NVI? – tower120 Jun 06 '18 at 14:42
  • 3
    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. – François Andrieux Jun 06 '18 at 14:44
  • 1
    @tower120 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 accidental – Killzone Kid Jun 06 '18 at 14:45
  • @KillzoneKid yes - but implement it as PUBLIC can easily be done accidental. – tower120 Jun 06 '18 at 14:46
  • @tower120 you can just as well "accidentally" override PUBLIC method as private, what's then? – Killzone Kid Jun 06 '18 at 14:49
  • @KillzoneKid then I can call it. This will be interface violation. I will be able to call intentionally hided function. – tower120 Jun 06 '18 at 14:50
  • @tower120 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 nevertheless – Killzone Kid Jun 06 '18 at 14:51
  • @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.... – tower120 Jun 06 '18 at 14:55
  • 1
    @tower120 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. – François Andrieux Jun 06 '18 at 14:56
  • @FrançoisAndrieux IOW this allowed, because not denied. Please, update answer with this. – tower120 Jun 06 '18 at 15:03
  • 1
    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. – Rakete1111 Jun 06 '18 at 17:16
  • @Rakete1111 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. – François Andrieux Jun 06 '18 at 17:25
  • @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? – Rakete1111 Jun 06 '18 at 17:30
  • @Rakete1111 I hadn't thought of why, but that makes sense. – François Andrieux Jun 06 '18 at 17:31
  • 2
    @FrançoisAndrieux Your avatar is a strangely good match for this answer. Well, given your activity in C++ tag, maybe not only this one... – Frax Jun 06 '18 at 21:20
  • "_overwriting a base's virtual_" overwriting or overriding? – curiousguy Jun 12 '18 at 22:55
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

rawberry
  • 192
  • 9
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.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • 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. – Frax Jun 06 '18 at 21:03
  • 2
    @Frax `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. – Cubbi Jun 06 '18 at 23:52
  • @Cubbi 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. – curiousguy Jun 30 '18 at 18:20
  • @curiousguy Are you inventing a new meaning for the word "access"? Its technical meaning (to which "private:" relates) applies when compiling a function call. – Cubbi Jul 01 '18 at 15:13
  • @Cubbi 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. – curiousguy Jul 01 '18 at 18:01
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.

jszpilewski
  • 1,632
  • 1
  • 21
  • 19
  • Yes, overriding, implementing, but not changing visibility. – tower120 Jun 06 '18 at 15:49
  • @tower120 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. – jszpilewski Jun 06 '18 at 16:01
  • @jszpilewski Access control (public, protected, private) is *not* the same as visibility. – curiousguy Jun 30 '18 at 18:18
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

Amit G.
  • 2,546
  • 2
  • 22
  • 30
  • If the function should not be overridden, just don't make it virtual. (Am I missing something?) – curiousguy Jun 30 '18 at 04:32
  • 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](https://learn.microsoft.com/en-us/visualstudio/code-quality/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. – Amit G. Jun 30 '18 at 19:44
  • I see but it the effort worth it? Also, making a class polymorphic as a (small) cost. – curiousguy Jun 30 '18 at 19:46
  • Overall, name hiding is dangerous and error-prone. In some cases, as a strong design-statement, I assume it worth the "effort". – Amit G. Jun 30 '18 at 19:59
  • 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. – curiousguy Jun 30 '18 at 20:14