On Thu, Jan 5, 2023 at 2:15 PM Brian Bi via Std-Proposals <std-proposals@lists.isocpp.org> wrote:


On Thu, Jan 5, 2023 at 5:09 PM Frederick Virchanza Gotham <cauldwell.thomas@gmail.com> wrote:
On Thu, Jan 5, 2023 at 10:01 PM Brian Bi <bbi5291@gmail.com> wrote:

> No.
>
> There's no legitimate reason to restrict what the author of B can make
> one of its non-virtual functions do. As such, there should not be a
> language-level mechanism for it.

The following program fails to compile:

struct A {
    virtual int Func(void) final { return 5; }
};

struct B : A {
    int Func(void) { return 6; }
};

int main(void)
{
    B obj;
}

Note here that the person who wrote 'B' didn't necessarily know that
'A' had a virtual function called 'Func', but nonetheless 'B' is
restricted in what it can do. So it won't be that much different
restricting non-virtual functions also.

I think you meant to send this to the list, not to me.

Sure, if you declare a virtual method `final`, then you restrict what derived classes can do, but the difference is that there's actually a legitimate reason for the restriction. If I have

    int foo(A& a) { return a.Func(); }

and `A::Func` is final, then the compiler can devirtualize the call. In addition, the writer of this code can rely on the guarantee that `a.Func()` has the same value and side effects as `a.A::Func()`. If any other functions in `A` call `A::Func`, then they, too, can rely on such guarantees.

On the other hand, if `Func` is non-virtual, then those guarantees already hold; having the ability to prevent a derived class from declaring a method with the same name does not strengthen them. As such, it is a restriction that gives nothing in return.

There are two things going on here:

1) final prevents calls to Func() being delegated to subclasses from the code under A's control (i.e., itself and parent scopes)
2) final prevents the name (actually the signature) from being reused in subclass scopes

1) is a legitimate goal of final. 2) is not.

So, the real question is: why does the specification make a half-assed attempt at making 2) true if it's not a goal of final? I say half-assed, since if you are really desperate to hide the name and reuse it in subclasses, you can workaround by adding a trivially defaulted parameter to the signature, at which point the compiler will stop complaining, even with calls that use exactly the same signature as before. If the point of final is not to restrict what you can do in subclasses, then why does the specification do exactly that?