Does final imply override?
Asked Answered
F

5

76

As I understand it, the override keyword states that a given declaration implements a base virtual method, and the compilation should fail if there is no matching base method found.

My understanding of the final keyword is that it tells the compiler that no class shall override this virtual function.

So is override final redundant? It seems to compile fine. What information does override final convey that final does not? What is the use case for such a combination?

Fiche answered 2/4, 2015 at 11:58 Comment(0)
S
70

final and override are independent requirements on either derived or base classes, respectively. Being final does not require the class/member to derive or override anything in the first place. Just be fully explicit with them (as appropriate).

There is one inconspicuous edge case where final is used without virtual. Consider

        void f()          final;  // (1)
        void f() override final;  // (2)
virtual void f() override final;  // (3)

virtual void f()          final;  // (4)

For (1), final always requires a virtual function, and for f to be implicitly virtual, it must be overriding a virtual base class function. Hence (1) and (3) are equivalent.

(2) obviously implies virtual so it is also equivalent to (1)/(3).

(4), however, is not equivalent to any of the above. It simply does not require a virtual base class version of f for the reasons that (1) does. (Which would also be pointless, if it did not have one.)

So your question as to where override matters specifically: when you mark the function virtual explicitly. Which you always should, for clarity. Hence (3) is the preferable style.

Soso answered 2/4, 2015 at 12:9 Comment(11)
I like your answer, but I'd like to clarify that from a practical point of view virtual void f() final override and void f() final are equivalent in the sense that both of them fail if they do not override something. final is only valid for virtual functions and the latter declaration of f is only virtual if it overrides a function. Error messages for the latter one may be less precise though.Chishima
the latter declaration expresses the intent a lot clearer - I don't think so. Your description has led me to prefer the first declaration style for the same reason I'd avoid to add virtual when overriding a virtual method: it adds no value. But it's of course better than writing virtual void foo() final;Tracheid
@Tracheid You don't think it expresses the intent clearer? Could you articulate exactly what the intent is that is expressed by the first declaration form?Soso
I think final has to be virtual after your description (I'm just learning these new features) and since you omit the virtual keyword, it wouldn't compile if it was not an override. Or maybe I am overlooking a case that could lead to unwanted behaviour? The use of non-overriding final virtual I'm just trying to figure out below Angew's answer ...Tracheid
I should add that bot variants you list in your answer are clear concerning their intent, only virtual void foo() final; would be unclear.Tracheid
@Tracheid If I regard the first line lexically, all I can immediately discern is that it cannot be overriden. The fact that final implies the function must be virtual to begin with is language-specific and not even that well-known, so the first line doesn't express its intent in a manner equally comprehensible and language-agnostic as the second one's.Soso
I have to admit that I have to get used to it first. Just one note, wouldn't it be better to suggest writing void foo() final override; to show intent clearly without introducing redundancies?Tracheid
@Tracheid IMO the virtual keyword is not redundant at all. According to your argument, override is just as redundant, and you seem to concede that it is not, at least not in the context of expressiveness.Soso
Most likely that is the reason why e. g. the Google C++ Style Guide suggests to "annotate overrides of virtual functions or virtual destructors with exactly one of an override or (less frequently) final specifier. Do not use virtual when declaring an override".Natka
Clang-tidy has a check (modernize-use-override) that complains when you use both override and final.Specialize
in the case where there is no base class method, the couple compilers I tried void f() final complains the function is not virtual, whereas void f() final override complains that there is not a method being overridden (and sometimes that the method is not virtual). override is most commonly used to catch errors with base class methods changing. In this case, we would want the error that comes with final override as a dev unfamiliar with the code might 'fix' the error by adding virtual to the method, instead of updating the method to match the signature in the base class.Phenomenon
T
25

final does not necessarily imply that the function is overridden. It's perfectly valid (if of somewhat dubious value) to declare a virtual function as final on its first declaration in the inheritance hierarchy.

One reason I can think of to create a virtual and immediately final function is if you want to prevent a derived class from giving the same name & parameters a different meaning.

Taveda answered 2/4, 2015 at 12:4 Comment(8)
+1 For showing a feasible use-case of a virtual/immediately-final function. I wonder if the compiler still generates a vtable when this is done though?Static
@Static That's up to the compiler. As far as the standard is concerned, it makes the class polymorphic, however - which means dynamic_cast must work for it, for example. I believe vtable implementations of virtual functions also need the vtable to make dynamic_cast work.Taveda
Bahh, I just posted a question based on my previous comment, but you pretty much just answered it here. Thanks.Static
If you want to prevent a derived class from giving the same name & parameters a different meaning, don't make it a virtual method. I don't see a feasible use-case in this vague description (unlike @Carlton), but I am really interested in one.Tracheid
@Tracheid I meant same non-qualified name, of course. Comments can't show code well, but I've written an example offsite. Derived2 cannot hide mustRemainBase.Taveda
Thanks for doing the extra work, but I'm still not able to get the point. The relation between Derived1 and Base1 I'd consider bad style, because overriding (hiding) is not really helpful for non-virtual methods. I'm looking on Columbo`s and your answer as to understand if a combination of virtual+final+override is really useful or just a waist of time.Tracheid
@Tracheid I wouldn't call hiding bad style, it has very legitimate use cases, particularly in combination with template techniques.Taveda
However, it still doesn't prevent a derived class from hiding a final virtual by adding an extra parameter. I.e. a derived class can declare void mustRemainBase(int = 0); and hide the original.Farahfarand
P
6

(Skip to the end to see the conclusion if you're in a hurry.)

Both override and final can appear only in declaration in a virtual function. And both key words can be used in the same function declaration, but whether it is useful to use them both depends on situations.

Take the following code as an example:

#include <iostream>
using std::cout; using std::endl;

struct B {
  virtual void f1() { cout << "B::f1() "; }
  virtual void f2() { cout << "B::f2() "; }
  virtual void f3() { cout << "B::f3() "; }
  virtual void f6() final { cout << "B::f6() "; }
  void f7() { cout << "B::f7() "; }
  void f8() { cout << "B::f8() "; }
  void f9() { cout << "B::f9() "; }
};

struct D : B {
  void f1() override { cout << "D::f1() "; }
  void f2() final { cout << "D::f2() "; }
  void f3() override final { cout << "D::f3() "; }  // need not have override
  // should have override, otherwise add new virtual function
  virtual void f4() final { cout << "D::f4() "; }
  //virtual void f5() override final;  // Error, no virtual function in base class
  //void f6(); // Error, override a final virtual function
  void f7() { cout << "D::f7() "; }
  virtual void f8() { cout << "D::f8() "; }
  //void f9() override;  // Error, override a nonvirtual function 
};

int main() {
  B b; D d;
  B *bp = &b, *bd = &d; D *dp = &d;
  bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
  bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
  dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
  return 0;
}

The output is

B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
  1. Compare f1() and f6(). We know that override and final is indepent sematically.

    • override means the function is overriding a virtual function in its base class. See f1() and f3().
    • final means the function cannot be overrided by its derived class. (But the function itself need not override a base class virtual function.) See f6() and f4().
  2. Compare f2() and f3(). We know that if a member function is declared without virtual and with final, it means that it already override a virtual function in base class. In this case, the key word override is redundant.

  3. Compare f4() and f5(). We know that if a member function is declared with virtualand if it is not the first virtual function in inheritance hierarchy, then we should use override to specify the override relationship. Otherwise, we may accidentally add new virtual function in derived class.

  4. Compare f1() and f7(). We know that any member function, not just virtual ones, can be overridden in derived class. What virtual specifies is polymorphism, which means the decision as to which function to run is delayed until run time instead of compile time. (This should be avoid in practice.)

  5. Compare f7() and f8(). We know that we can even override a base class function and make it a new virtual one. (Which means any member function f8() of class derived from D will be virtual.) (This should be avoid in practice too.)

  6. Compare f7() and f9(). We know that override can help us find the error when we want to override a virtual function in derived class while forgot to add key word virtual in base class.

In conclusion, the best practice in my own view is:

  • only use virtual in declaration of the first virtual function in base class;
  • always use override to specify override virtual function in derived class, unless final is also specified.
Precinct answered 25/2, 2016 at 9:15 Comment(0)
M
5

The following code (with the final specifier) compiles. But compilation fails when final is replaced with override final. Thus override final conveys more information (and prevents compilation) than just final.

class Base
{
public:
    virtual ~Base() {}
};

class Derived : public Base
{
public:
    virtual void foo() final
    {
        std::cout << "in Derived foo\n";
    }
};

Essentially, override final says this method cannot be overridden in any derived class and this method overrides a virtual method in a base class. final alone doesn't specify the base class overriding part.

Meseems answered 6/9, 2015 at 8:28 Comment(1)
"final alone doesn't specify the base class overriding part." Correct, but void foo() final does.Supply
W
1

No final does not necessarily imply override. In fact, you could declare a virtual function that you immediately declare final see here. The final keyword simply states that no derived class can create an override of this function.

The override keyword is important in that it enforces that you are indeed actually overriding a virtual function (instead of declaring a new unrelated one). See this post regarding override

So long story short, they each serve their own particular purpose, and it is often correct to use both.

Witkowski answered 2/4, 2015 at 12:4 Comment(2)
Hmm, I wish that first example you posted wouldn't compile, because the way I see it declaring a virtual function and making it final in the same breath makes absolutely no sense at all.Guthrun
cpp core guidelines say only use one github.com/isocpp/CppCoreGuidelines/blob/master/…Amphiprostyle

© 2022 - 2024 — McMap. All rights reserved.