On Wed, Jul 15, 2020 at 2:11 AM Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Tue, Jul 14, 2020 at 4:56 AM Paweł Benetkiewicz via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
>
> Modern C++ compilers use LARL as resolving ambiguity may (and often do) require a separate pass. Or many passes. In fact, C++ can require an infinite lookahead.
> Unconditional final remains the same, conditional final may require additional pass, same as any other `constraint-logical-or-expression`.

Um, no it doesn't. Constraints of any kind do not require parsing the
definition of the construct being constrained. Constraints are only
based on the declaration. You cannot constrain a class template `T` on
the properties of `T` itself.

+1.
 
I think the point you missed is that, if `final` is conditional, then
to answer this question, `Derived` must be fully defined (if the
condition relies on any property of `Derived` other than that it
exists). Therefore, if a compile error has not happened by the time
you're asking this question, then `Derived` has already inherited from
`Base`.
So what's the point of asking a question whose answer is *certainly* "yes"?

+1.

>
> It could be also split into two separate type traits like is_unconditionally_final and is_conditionally_final, but it could break backwards compatibility.
>
> > I have never *heard *of "conditional inheritance."
> Just first few googled examples:
> - https://stackoverflow.com/questions/5040831/restrict-the-classes-that-may-implement-an-interface

// https://godbolt.org/z/6T4vq6
template<class T>
struct Foo {
protected:
    template<class This>
    explicit Foo(This *self) {
        static_assert(std::is_base_of_v<T, This>);
        assert(self == this);
    }
};

struct Bar {};
struct Baz1 : public Bar, public Foo<Bar> {  // OK
    explicit Baz1() : Foo(this) {}
};

> - https://stackoverflow.com/questions/5767160/allowing-implementing-interface-only-for-specific-classes

Make the constructor private and `friend` only the allowed classes.

> - https://stackoverflow.com/questions/26465237/allow-a-mock-class-to-inherit-from-a-final-class

> If the class is final, you do not need to derive from it. If you do need to derive from it, it is not final. Pick one.
 
> - https://stackoverflow.com/questions/33007574/restrict-classes-that-can-implement-interface-in-java

// https://godbolt.org/z/TscWzv
struct Iface { virtual ~Iface() = default; };
struct Superclass {};

template<class T>
struct IfaceImpl : Iface, T {
    template<class... Args>
    explicit IfaceImpl(Args&&... args) : T(std::forward<Args>(args)...) {}
};

template<class T, class... Args> requires std::is_base_of_v<Superclass, T>
std::unique_ptr<Iface> make_iface(Args&&... args) {
    return std::make_unique<IfaceImpl<T>>(std::forward<Args>(args)...);
}

struct Class {};
struct Subclass : Superclass {};

int main() {
    auto x = make_iface<Subclass>();  // OK
    auto y = make_iface<Class>();  // ERROR
}

[...]
Class `final` is already a dubious prospect in C++ because C++ uses
inheritance for things that other languages do not. C++ uses
inheritance where other languages might use mixins or partial classes
for example. C++ users sometimes employ inheritance to gain access to
the empty base optimization when applicable. C++ users sometimes
employ inheritance for test driven frameworks (as shown by one of your
questions).

Basically, declaring a class to be `final` should *always* be looked
on as a code smell in C++. It makes about as much sense in C++ to
declare that a class cannot be a base class of another class as it
does to declare that a class cannot be a *member* of another class.

Here I disagree with Jason. The `final` modifier is useful (or at least harmless) in C++ pretty much exactly whenever classical polymorphism is useful. They say: "Make every polymorphic class either abstract or final"; I agree with that. Now, it's true that you could mark the leaf class /*final*/ instead of final and you'd reap pretty much all the same benefits — the keyword doesn't really do anything except documentation — but at least it doesn't prevent you from doing anything that you should be wanting to do in the first place. (It prevents you from inheriting from the final class, which is something you shouldn't want to do.)
I agree with your last sentence, btw. If you're using classical polymorphism, it also does not make sense to declare a class instance as a member of another class.

struct FooFace { virtual void f() = 0; ... };
struct FooImpl : FooFace {
    BarImpl bar;  // should almost certainly be `BarFace *bar` for dependency injection
    ...
};

Classes (like `std::string`) that are intended to be used as members, value-semantically, should not inherit from anything and should not be inherited from (except for the Empty Base Class Optimization) and therefore shouldn't mess around with `final`, or `virtual`, or `protected`, or any of the other stuff on the "Java" side of C++.


> > Furthermore: the proposal states "Disadvantages: none", "Design trade-offs: none". While the latter may end up being somewhat accurate, the first one is complete nonsense, rendering this proposal technically ill-formed.
> So will it be ok if I change 'Disadvantages' from "None" to "Proposal increases complexity of language by adding optional `final-clause`."? I've read a few proposals (both PR… and N…) and I didn't find such sentences.

It's more that, if you're going to have a list of "Disadvantages" or
"design trade-offs", then it needs to actually list them honestly.

By "design tradeoffs," we mean "I considered doing the feature with syntax/semantics X instead, which would be nice because Y, but I rejected that alternative design because Z." For example, you chose to mess with the `is_final` type-trait instead of ignoring the type-traits; why? You chose to overload `is_final` instead of introducing a new trait; why? And so on and so forth.  (But of course all this is moot at the moment because the core of your proposal, the whole "use names without declaring them" bit, doesn't work.)

–Arthur