Topic: final (Was: Ideas for new keywords and features)


Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/25
Raw View
blargg wrote:

> In article <3798A95C.7B02@wanadoo.fr>, Valentin Bonnard
> <Bonnard.V@wanadoo.fr> wrote:

> > - a function should be able to be declared final:
> >
> >     struct U {

> >         void foo () /* non virtual */ final; // legal
> >         virtual void foobar () /* non overrider */ final; // legal
>
> Why should these be legal? What meaning does final have for foo()? Isn't
> it redundant in foobar()?

final is a cv-qualifier, and as such supports the

   ret-type function-name (argument-list) cv-qualification

syntax.

> >     };
> >
> >     struct V : U {

> >         // foo not really inherited here
> >         override void foobar (); // required for class V
> >                                  // to be concrete
> >     };
>
> You lost me here. If foobar() is final in U,

It isn't...

> then it cannot be overridden
> in a derived class,

...so it can.

The this pointer is final in U::foobar().
U::foobar() isn't final.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/23
Raw View
blargg wrote:

> (seems this didn't go through the first time I sent it)

Moderation delays ?

>     http://www.slack.net/~ant/cpp/final.html
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   (recommended reading that I won't repeat here)

Some comments:

- final T should be legal for any T:

    final int i; // ok

- final T is T if T is a non class type:

    int* p;
    final int& i = *p;

- final T is T if T is final U:

    final final int i; // syntax error, mustn't repeat final

- Container::value_type should be final qualified,
  as iterator_traits<>::value_type when applicable

    vector<string> vs;
    final string& r = vs.front (); // ok

- final conversions should be the dual of other cv-qualifier
  conversions:

    final B** ppfb;
    B** ppb = ppfb; // error
    B*const* pcpb = ppfb; // ok

- this implies that final doesn't change pointer
  representation, a requirement which might be
  explicitly spelled out or left implicit

- final should support covariant return types:

    struct B {
        virtual B* foo ();
    };
    struct D : B {
        override final B* foo ();
    };

- a function should be able to be declared final:

    struct U {
        final void bar () /* non virtual */; // you said not legal
        void foo () /* non virtual */ final; // legal
        virtual void foobar () /* non overrider */ final; // legal
    };

    struct V : U {
        void bar (); // hides U::Bar
        // foo not really inherited here
        override void foobar (); // required for class V
                                 // to be concrete
    };

    V v;
    v.foo (); // error
    v.foobar (); // calls V::foobar

    struct W : U {
    }; // ok so far

    W w; // error, W is abstract
         // because function foobar is pure virtual in W

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Lisa Lippincott <lisa_lippincott@advisories.com>
Date: 1999/07/24
Raw View
I think the word "final" is being overburdened in this discussion.  Given
classes X and Y, can one declare a member function X::F with all of these
properties?

   X::F may only be called on objects of exact type X
   X::F returns a pointer to an object of exact type Y
   X::F may not be overriden

                                                       --Lisa Lippincott


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/07/24
Raw View
In article <230719991805038495%lisa_lippincott@advisories.com>, Lisa
Lippincott <lisa_lippincott@advisories.com> wrote:

> I think the word "final" is being overburdened in this discussion.  Given
> classes X and Y, can one declare a member function X::F with all of these
> properties?
>
>    X::F may only be called on objects of exact type X
>    X::F returns a pointer to an object of exact type Y
>    X::F may not be overriden

I think that semantics are the current focus of discussion. Whatever gets
the point across syntactically is all that matters. As you say, perhaps
there should be more than one syntactic term for the different semantic
applications of the concept.

I'd rather not bother with worrying about syntax until I am convinced that
the concept is a valid one with regards to semantics. In effect, syntax is
like the "implementation" of the design. I don't want to rush into
"implementation" just yet.

(I've expanded the page about "final" to include more examples and idea -
http://www.slack.net/~ant/cpp/final.html ) <- haha, there's an example of
having to add an extra space to prevent ambiguities, much like foo<bar<X>
>  hehehe
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/25
Raw View
Let me add:

- you should be able to overload on final:

    struct B {
        void foo () final; // (1)
        void foo ();       // (2)
    };

    B b;
    b.foo (); // calls to (1) prefered
    B& r = b;
    r.foo (); // calls to (2) forced
              // no overload resolution here

- every overrider of a function of final type
  shall be final itself (change from previous
  description). final virtual functions are
  function which shall be overriden in every
  concrete derived class:

    struct B {
        virtual void foo () final;
    };

    struct D : B {
        override void foo (); // error,
        // no virtual non final function in B

        override void foo () final; // ok
    };

  This is useful in practice. (Today, I use
  documentation to make this kind of requirements.)

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/07/25
Raw View
In article <3798A95C.7B02@wanadoo.fr>, Valentin Bonnard
<Bonnard.V@wanadoo.fr> wrote:

> blargg wrote:
>
> > (seems this didn't go through the first time I sent it)
>
> Moderation delays ?
>
> >     http://www.slack.net/~ant/cpp/final.html
>       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>    (recommended reading that I won't repeat here)
>
> Some comments:

[good comments snipped - someone's though about this before, I think :-) ]

> - a function should be able to be declared final:
>
>     struct U {
>         final void bar () /* non virtual */; // you said not legal

Right, because bar() isn't overriding anything, and this the final is
meaningless (and potentially misleading).

>         void foo () /* non virtual */ final; // legal
>         virtual void foobar () /* non overrider */ final; // legal

Why should these be legal? What meaning does final have for foo()? Isn't
it redundant in foobar()?

>     };
>
>     struct V : U {
>         void bar (); // hides U::Bar
>         // foo not really inherited here
>         override void foobar (); // required for class V
>                                  // to be concrete
>     };

You lost me here. If foobar() is final in U, then it cannot be overridden
in a derived class, and in U it is defined anyway, so where does the
abstractness come from?

>     V v;
>     v.foo (); // error
>     v.foobar (); // calls V::foobar
>
>     struct W : U {
>     }; // ok so far
>
>     W w; // error, W is abstract
>          // because function foobar is pure virtual in W

A thought about override came from your message - perhaps overriding
shouldn't take place at all with out it, and if there is a base virtual
function with the same name and signature, it should be hidden, instead of
it being an error:

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

    struct D : B {
        override void f();
    };

    struct DD : D {
        void f(); // hides D::f - not an error
    };

I can see how this could be important for templates or other users that
want to resolve a clash by simply hiding the base class virtual function.

Too bad all this has to take into account current code, if it is a
realistic proposal (even if just informal).
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/07/25
Raw View
In article <3798D5E3.627A@wanadoo.fr>, Valentin Bonnard
<Bonnard.V@wanadoo.fr> wrote:

> Let me add:
>
> - you should be able to overload on final:
>
>     struct B {
>         void foo () final; // (1)
>         void foo ();       // (2)
>     };
>
>     B b;
>     b.foo (); // calls to (1) prefered
>     B& r = b;
>     r.foo (); // calls to (2) forced
>               // no overload resolution here

Good, good! Be able to optimize if the exact type of object is known at
compile-time, otherwise use something else. Again, compilers could use the
same mechanism to internally implement the virtual call optimization when
the static type of the object is known.


In case anyone didn't notice, another addition was that any
references/pointers to object of a "final" class type would always be
final, whether or not they were declared as such. Following this, built-in
types would always have the final type (as Valentin noted recently).

    final struct Foo { };

    struct Bar { };

    void f( Foo& foo, Bar& bar, int& i )
    {
        Foo final& ffoo = foo; // OK - Foo couldn't refer to anything derived
        Bar final& fbar = bar; // error
        int final& fi = i;     // OK - int can't be derived from (duh!)
    }

> - every overrider of a function of final type
>   shall be final itself (change from previous
>   description). final virtual functions are
>   function which shall be overriden in every
>   concrete derived class:
>
>     struct B {
>         virtual void foo () final;
>     };
>
>     struct D : B {
>         override void foo (); // error,
>         // no virtual non final function in B
>
>         override void foo () final; // ok
>     };
>
>   This is useful in practice. (Today, I use
>   documentation to make this kind of requirements.)

Interesting. So, for example, a persistence mechanism with a virtual
"output" function could require derived classes to implement this, the
premise being that it would likely be an error if they didn't:

    class Persistent {
        virtual void output() const final; // too many keywords! :-)
    };

    // OK
    class Foo : public Persistent {
        override void output() const final;
    };

    // error - didn't override "output()"
    class Bar : public Persistent {
    };
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/22
Raw View
farve@my-deja.com wrote:

> Frankly, I'm surprised people are having so much trouble understanding
> this, and seeing the obvious merits of it.  I've worked in the compiler
> industry most of my career, so cut the patronizing, and use your
> brains, will ya?

Interresting. Have you read your own article ?

Hint: What do you write about the C++ committee ?

> It would generate a compile
> time error, when you tried to pass a BaseClass* to a final BaseClass*,

Corollary: we need another type of cast, say final_cast
(exact_cast ?) to convert from T* to final T*.

> Face it, C++ sucks.

It doesn't. This paragraph is just a troll. (And one
shouldn't feed trolls.)

> >>>>
> And wouldn't it be nice if somebody told you about templates?
> <<<<
>
> Excuse me?

The end of your first article could be taken (and in fact has
been taken) as showing a misunderstanding of what templates are
and how they should be used.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/22
Raw View
Bill Wade wrote:

> In
> principle a compiler could recognize the use of the pattern and perform
> 'final' optimizations.
[...]
> In principle a compiler could detect this and do 'final' optimizations.
[...]
> In principle
> a compiler could avoid doing the test

Are you serious or are you kidding ?

(In any cases your article is fun.)

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/07/22
Raw View
Valentin Bonnard wrote in message <3796F6F8.727B@wanadoo.fr>...

>Are you serious or are you kidding ?


Mostly kidding.  Some things that I wouldn't be surprised to see in the next
five years or so:

1) A compiler implementing a __final keyword for one or more of the forms we
discussed.

Useful because of the added opportunity to inline function calls.

2) A compiler that performs dead-code elimination on the following
  int k = i / j;
  if(j == 0) EliminateThis();
slightly useful (in C, less in C++).  For instance if a compiler can be sure
of the possible range of a switch() argument, it might be able to eliminate
the code that tests for out-of-range.

3) A translator which is C++ smart (say smart enough to do a good job with
'export') is probably smart enough to identify classes which have no
descendants.  For pointers to such classes the linker might change virtual
dispatch to static dispatch.  I think this is possible but not likely.  The
benefit is small, and the widespread use of shared (or .dll) libraries seems
to make it difficult to be sure that a class was a leaf class.

>(In any cases your article is fun.)


Thanks.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]