23

C++11 added final.

Finally!

I understand final does two things:

  • Makes a class non-inheritable.
  • Makes (virtual) functions in a class non-overridable (in a derived class).

Both of these seem independent of each other. But take for example the following:

class Foo
{
    public:
    virtual void bar()
    {
        //do something unimportant.
    }
};
class Baz final : public Foo
{
    public:
    void bar() /*final*/ override
    {
        //do something more important than Foo's bar.
    }
};

From above, I believe Baz being final, I should NOT need to specify that its virtual member function bar is also final. Since Baz cannot be inherited, the question of overriding bar goes out of scope. However my compiler VC++ 2015, is very quiet about this. I have not tested this on any others at the moment.

I would be glad if someone could shed some light on this topic. A quote from the standard (if any) would be extremely appreciated. Also please state any corner cases that I am unaware of, that may cause my logical belief to fail.

So, my question is: Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.


The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization. Any help is appreciated.

Anirban Sarkar
  • 796
  • 6
  • 14
  • 2
    final functions are de-virtualized only when static type matches the one where function is final. Happenes rarely, and is often an indication of the fact that no virtual is needed in the first place. – SergeyA Apr 27 '16 at 16:04
  • @SergeyA Yes. That's why I wrote qualified. – Anirban Sarkar Apr 27 '16 at 16:28
  • 2
    If the compiler can guarantee that it does not need a virtual call then it will not plant a virtual call (as an optimization). The compiler is clever and already did this pre the introduction of final. But I suppose it is easier now. – Martin York Apr 27 '16 at 16:58
  • @AnirbanSarkar Can you clarify what you mean by your compiler "is very quiet about this"? – KyleKnoepfel Apr 27 '16 at 17:01
  • It does not say anything, that whether I explicitly need to state a `final` keyword in the declaration sequence of the member function in question. _If I don't it does not complain. If I do it does not complain._ At first glance it seemed so implicit, I omitted writing `final`. And there is no way to check if the function is indeed marked `final`. So suspicions arose and I decided to post this. – Anirban Sarkar Apr 27 '16 at 17:26
  • @KyleKnoepfel I didn't find helpful definitions in this context on the web either. – Anirban Sarkar Apr 27 '16 at 18:21
  • 2
    If you mark your class final, you can't inherit from it and hence you cant override any of its memberfunctions, so why bother if a member function is final or not? – MikeMB Apr 27 '16 at 18:30
  • More specifically because my compiler doesn't complain in either case, which is annoying. – Anirban Sarkar Apr 28 '16 at 02:19
  • 2
    The compiler is allowed to treat all member functions of a `final` class as if they were declared `final` – M.M Apr 28 '16 at 02:24
  • @M.M So does it or does it not treat them as such? Or is it all implementation dependent? That is my question. – Anirban Sarkar Apr 28 '16 at 02:27
  • IDK what your particular compiler does. The behaviour defined by the C++ Standard for a program is identical whether or not you write `final` on the member functions of a `final` class. – M.M Apr 28 '16 at 02:30
  • @M.M OK. Thanks! That actually solves my question. Can you post a quote/reference to the standard that actually states this? – Anirban Sarkar Apr 28 '16 at 02:32
  • I'm confused. Aren't all of these things the same thing? If you can't derive from `Class` then you can't override its virtual member functions either. – Lightness Races in Orbit May 06 '16 at 14:03
  • @LightnessRacesinOrbit Yes. That's why I asked if final specifier (applied to member virtual functions) could be implied in such a case. – Anirban Sarkar May 11 '16 at 16:53

3 Answers3

20

The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.

Do they? "De-virtualization" is not part of the C++ standard. Or at least, not really.

De-virtualization is merely a consequence of the "as if" rule, which states that the implementation can do whatever it likes so long as the implementation behaves "as if" it is doing what the standard says.

If the compiler can detect at compile-time that a particular call to a virtual member function, through a polymorphic type, will undeniably call a specific version of that function, then it is allowed to avoid using the virtual dispatching logic and calling the function statically. That's behaving "as if" it had used the virtual dispatching logic, since the compiler can prove that this is the function that would have been called.

As such, the standard does not define when de-virtualization is allowed/forbidden. A compiler, upon inlining a function that takes a pointer to a base class type, may find that the pointer being passed is pointing to a stack variable local declared in the function that it is being inlined within. Or that the compiler can trace down a particular inline/call graph to the point of origin for a particular polymorphic pointer/reference. In those cases, the compiler can de-virtualize calls into that type. But only if it's smart enough to do so.

Will a compiler devirtualize all virtual function calls to a final class, regardless of whether those methods are declared final themselves? It may. It may not. It may not even devirtualize any calls to methods declared final on the polymorphic type. That's a valid (if not particularly bright) implementation.

The question you're asking is implementation specific. It can vary from compiler to compiler.

However, a class being declared final, as you pointed out, ought to be sufficient information for the compiler to devirtualize all calls to pointers/references to the final class type. If a compiler doesn't do so, then that's a quality-of-implementation issue, not a standards one.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Nope. The entire virtualization mechanism (done with virtual tables and pointers) is not part of the standard. I was hoping the implementors would post details about the implementation details they implemented. Anyway, +1 for the clear answer. – Anirban Sarkar Apr 28 '16 at 02:40
7

To quote the draft C++ standard from here [class.virtual/4]:

If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.

And here [class/3]:

If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (Clause [class.derived]), the program is ill-formed.

So, in answer to the question;

Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.

So, at least not formally. Any attempt to violate either rule will have the same result in both cases; the program is ill-formed and won't compile. A final class means the class cannot be derived from, so as a consequence of this, its virtual methods cannot be overridden.

Should it? At least formally, probably not; they are related but they are not the same thing. There is also no need formally require the one to imply the other, the effect follows naturally. Any violations have the same result, a failed compilation (hopefully with appropriate error messages to distinguish the two).


To touch on your motivation for the query and the de-virtualization of the virtual calls. This is not always immediately affected by the final of the class nor method (albeit they offer help), the normal rules of the virtual functions and class hierarchy apply.

If the compiler can determine that at runtime a particular method will always be called (e.g. with an automatic object, i.e. "on the stack"), it could apply such an optimisation anyway, irrespective of the method being marked final or not. These optimisations fall under the "as-if" rule, that allow the compiler to apply any transformation so long as the observable behaviour is as-if the original code had been executed.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • Uh? How is it _ill-formed_? The `final class` is supposed to imply **its own** virtually inherited member function as `final`. The example above compiles in both test cases: When the `final` specifier is attached to the virtual function and when not (without an error/warning/informational messages whatsoever). Please explain. – Anirban Sarkar Apr 28 '16 at 02:25
  • Yes, the standard wording you (as asked for) relates to when the an attempt it made to override a final method or have a final class as a base. That is the purpose of final. The final keyword can be combined, there is no problem with that, the two cases don't mean the same thing. It could even be advised if you have concerns about future maintenance of the code. The optimizations you mention are a quality of implementation issues, and have been around long before final was added to the standard. – Niall Apr 28 '16 at 04:34
3

Does a final class implicitly imply its virtual functions to be final as well?

[...]

I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.

Yes, it does, for the purposes of de-virtualization, in all major compilers (including MSVC):

struct B                   { virtual void f() = 0;   };

struct D1       : public B {         void f();       };
struct D2       : public B {         void f() final; };
struct D3 final : public B {         void f();       };

void f1(D1& x) { x.f(); } // Not de-virtualized
void f2(D2& x) { x.f(); } //     De-virtualized
void f3(D3& x) { x.f(); } //     De-virtualized
Acorn
  • 24,970
  • 5
  • 40
  • 69