28

I am reading an awesome awesome C++11 tutorial and the author provides this example while explaining the final keyword:

struct B {
    virtual void f() const final;   // do not override
    virtual void g();
};
struct D : B {
    void f() const;     // error: D::f attempts to override final B::f
    void g();       // OK
};

So does it make sense using here the final keyword? In my opinion you can just avoid using the virtual keyword here and prevent f() from being overridden.

JFMR
  • 23,265
  • 4
  • 52
  • 76
Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • 3
    I don't want to knock it, since a guru may come along and present a use-case where it solves some really obscure compilation error... But it looks like a silly thing to do. – StoryTeller - Unslander Monica May 24 '17 at 08:46
  • 2
    Possible duplicate of [Is there any sense in marking a base class function as both virtual and final?](https://stackoverflow.com/questions/16739135/is-there-any-sense-in-marking-a-base-class-function-as-both-virtual-and-final) – Toby Speight May 24 '17 at 16:56
  • Actually there are more duplicates, e.g. [What's the point of a final virtual function?](https://stackoverflow.com/questions/11704406/) – Ruslan May 24 '17 at 17:31
  • Why are there 3 close requests? How is the question **not** legitimate? – curiousguy May 28 '17 at 21:32

3 Answers3

34

If you don't mark the function as virtual and final then the child-class can still implement the function and hide the base-class function.

By making the function virtual and final the child-class can not override or hide the function.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 3
    I knew a guru would come along. Although I think this is a bit too much in the way of overhead (that may or may not be elided), just to prevent hiding. +1. – StoryTeller - Unslander Monica May 24 '17 at 08:48
  • 3
    Apparently there is consensus on the matter, but I for one would like an explanation on how doing nothing useful *and* breaking another language feature would be considered a benefit. – Quentin May 24 '17 at 08:56
  • 1
    @Quentin its removing the possibility of ambiguity, e.g. in the case of `template void doStuff(DerivedFromB & b) { b.f(); }`, you can be 100% sure where that `f()` call goes – Caleth May 24 '17 at 09:48
  • @Caleth why would you make it a template and perform an unqualified member call if you want a specific `f` to be called? – Quentin May 24 '17 at 09:50
  • @Quentin that's a minimal contrived example, but it applies where you have other reasons for the template – Caleth May 24 '17 at 10:00
  • 1
    @Caleth `b.B::f();` will work as you expect even if the static type of `b` is `D`. – Quentin May 24 '17 at 10:13
  • I think we disagree with what "useful" includes. I consider turning run-time logic errors into compile-time errors "useful". The point is that the writer of B can constrain the writer of D. – Caleth May 24 '17 at 10:25
  • @Caleth I have no idea what "runtime logic error" you're talking about. You want to call `B::f`, just call it. I appreciate your effort at coming up with an example, but here there is simply no point in writing a completely general `b.f()` (i.e. call the statically most-derived `f`), only to cripple the ability to actually hide `f` afterwards. The result is the same, but now the developper of `D` and `D`'s descendants gets that `virtual` kludge and cannot use `f` anymore. – Quentin May 24 '17 at 10:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/145009/discussion-between-caleth-and-quentin). – Caleth May 24 '17 at 10:43
  • 3
    @Caleth: `class D : public B { virtual void f(int=0); }`. Who says I can't "override" `B::f` ? Quentin's solution `b.B::f()` _does_ work as intended here. – MSalters May 24 '17 at 10:52
11

Yes! In the example you provide, the final keyword prevents any derived classes from overriding f() as you correctly say. If the function is non-virtual, D:f() is allowed to hide the base class version of the function:

struct B {
    void f() const;   // do not override
    virtual void g();
};
struct D : B {
    void f() const; // OK!
    void g();       // OK
};

By making f() a virtual and final function, any attempt at overriding or hiding causes a compile error.

Karl Nicoll
  • 16,090
  • 3
  • 51
  • 65
3

Your intuition is right: making a function virtual only to immediately cap it with final has no benefit over a non-virtual function. This is just a short sample snippet to demonstrate the feature.

Additionally, as described in other answers, this actually breaks function hiding -- you won't ever be able to have an f function with the same parameter list in D or any of its derived classes.
This is a trade-off to be done when you decide to cap f in your model. Since there is no way to perform an actual virtual call here, you essentially have the disadvantage and no benefit.

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 1
    I thought of name hiding. Thus you prevent the derived class to hide f(). If you declare f() without virtual keyword, it can be hidden in derived class I guess. – Eduard Rostomyan May 24 '17 at 08:47
  • Comments are, as always, much appreciated with downvotes. – Quentin May 24 '17 at 14:04
  • 1
    I think the downvotes are due to your attitude towards disabling name hiding. I suppose this feature can actually be used for some special purpose, although I can't name any such purpose off the top of my head. – Ruslan May 24 '17 at 17:29
  • @Ruslan well, anything to do with static polymorphism for a start. I don't know, disabling arbitrary features because you don't know a use for them and claiming it's for the better sounds very short-sighted to me. Is there a `goto`-like taboo on function hiding that I'd have missed? – Quentin May 25 '17 at 10:16
  • By the feature I don't mean "function name hiding" — I mean "disabling function name hiding". Of course I do know uses for name hiding itself. – Ruslan May 25 '17 at 10:26
  • @Ruslan oh, my bad :) – Quentin May 25 '17 at 10:31