Topic: Bad programming practices in stdlib.


Author: rdamon@BeltronicsInspection.com (Richard Damon)
Date: 1998/02/01
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote:

>Bradd W. Szonye <bradds@concentric.net> wrote:
>: Would an implementation that gave these things virtual destructors be
>: conforming?
>
>I think not, because whether the destructor is virtual or not
>can be observed like this:
>
>class A : public forward_iterator_tag {
> public:
>   ~A() { cout << "Destructor is virtual\n"; }
>};
>
>forward_iterator_tag* ptr = new A;
>ptr -> ~forward_iterator_tag();
>
>As far as I understand, this program is well-formed whether the
>destructor is virtual or not, but the output must be different for
>the two cases. Therefore you can't just put a virtual destructor,
>justifying it by the as-if rule.
>
>: Would then optimizing the actual objects out of the generated
>: code be conforming?
>
>I think yes.
>
>Oleg.

Wrong,
 Since the spec does not include the virtual destructor deleting an A through a
pointer to forward_iterator_tag invokes undefined behavior, thus it is not
well-formed.
--
richard_damon@iname.com (Redirector to my current best Mailbox)
rdamon@beltronicsInspection.com (Work Adddress)
Richad_Damon@msn.com (Just for Fun)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/02/01
Raw View
Oleg Zabluda wrote:
>
> Bradd W. Szonye <bradds@concentric.net> wrote:
> : Would an implementation that gave these things virtual destructors be
> : conforming?
>
> I think not,

You are right:

2 An  implementation  can declare additional non-virtual member function
  signatures within a class:

  --by adding  arguments  with  default  values  to  a  member  function
    signature;27) The same latitude does not extend to  the  implementa-
    tion of virtual or global functions, however.> 2 An  implementation
can declare additional non-virtual member function
>   signatures within a class:
>
>   --by adding  arguments  with  default  values  to  a  member  function
>     signature;27) The same latitude does not extend to  the  implementa-
>     tion of virtual or global functions, however.


> because whether the destructor is virtual or not
> can be observed like this:

It cannot.

> class A : public forward_iterator_tag {
>  public:
>    ~A() { cout << "Destructor is virtual\n"; }
> };

No matter what you do, "Destructor is virtual\n" has to be printed.

> As far as I understand, this program is well-formed whether the
> destructor is virtual or not,

Yes

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/02/02
Raw View
Valentin Bonnard <bonnardv@pratique.fr> wrote:
: Oleg Zabluda wrote:
: >
: > Bradd W. Szonye <bradds@concentric.net> wrote:
: > : Would an implementation that gave these things virtual destructors be
: > : conforming?
: >
: > I think not, because whether the destructor is virtual or not
: > can be observed like this:

: It cannot.

Why?

: > class A : public forward_iterator_tag {
: >  public:
: >    ~A() { cout << "Destructor is virtual\n"; }
: > };

[ O.Z -- Apparantly the following snipped:    ]
[                                             ]
[ forward_iterator_tag* ptr = new A;          ]
[ ptr -> ~forward_iterator_tag();             ]

: No matter what you do, "Destructor is virtual\n" has to be printed.

Why?

Just a clarification, in case it wasn't clear the first time. All
throughout the post, I was assuming that "as-if" rule superseeds
all other language rules. If there is no well-formed program which
can tell if the destructor is virtual or not, then an implementation
is free to do what it likes. Since the infamous deleting is an undefined
behaviour, it seems at first that no well-formed program can tell.
Therefore the Bradd's question. Here I came up with the example of what
seems to me as a well-formed program, which can tell. Therefore the answer.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/02/02
Raw View
Richard Damon <rdamon@BeltronicsInspection.com> wrote:
: Oleg Zabluda <zabluda@math.psu.edu> wrote:

: >Bradd W. Szonye <bradds@concentric.net> wrote:
: >: Would an implementation that gave these things virtual destructors be
: >: conforming?
: >
: >I think not, because whether the destructor is virtual or not
: >can be observed like this:
: >
: >class A : public forward_iterator_tag {
: > public:
: >   ~A() { cout << "Destructor is virtual\n"; }
: >};
: >
: >forward_iterator_tag* ptr = new A;
: >ptr -> ~forward_iterator_tag();
: >
: >As far as I understand, this program is well-formed whether the
: >destructor is virtual or not, [...]

: Wrong,
:  Since the spec does not include the virtual destructor deleting an A through a
: pointer to forward_iterator_tag invokes undefined behavior, thus it is not
: well-formed.

Nowhere do I delete anything through anything.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/29
Raw View
J. Kanze <kanze@gabi-soft.fr> wrote:
: Oleg Zabluda <zabluda@math.psu.edu> writes:

: |>  Let's review the stuff I was complaining about, shall we:
: |>
: |>  1.
: |>
: |>  struct input_iterator_tag {};
: |>  struct forward_iterator_tag : public const input_iterator_tag {};
: |>
: |>  Why not just put a virtual destructor in the input_iterator_tag?
: |>  Who are we kidding anyway? It's not like input_iterator_tag or
: |>  forward_iterator_tag have zero size or anything. Besides, normally
: |>  we would never instantiate either of them. It's there simply to use
: |>  the overload resolution mechanism for compile time binding. If some
: |>  poor dude somehow manages to instantiate it, we can rely on a good
: |>  optimizer or compiler magic to optimize the empty destructors away.
: |>  In retrurn we get the situation when if a user's code looks innocent
: |>  to someone with no more then one Ph.D, it will probably be legal as
: |>  well.

: I suspect that the reason is that putting a virtual destructor in them
: would cause all of the iterators derived from them to contain a vptr.
: In some cases, this would double the size of the iterator.

Actually, I would expect the space occupied by a stand-alone iterator
(not put into array) to either stay unchanged (4 byte alingment, 4 byte
pointers -- Sun Classic), quadrupple (no alingment, 4 byte
pointers -- IBM PC), octupple :-) (no alingment, 8 byte pointers -- maybe
future Intel CPU), or double (4 byte aligment, 8 byte pointers -- probably
most 64 bit RISC cpu's). However, theoretically a compiler can totally
eliminate them, since they have no internal state. All instances of
the types we are talking about are identical. There is no reason to
either construct or pass them anywhere.

: As for the undefined behavior: there are so many ways to get undefined
: behavior from the standard containers that one way more isn't going to
: make any real difference.  There are, in fact, so many ways of
: accidentally getting undefined behavior from the standard containers
: that no reasonable person will use them in production code.  Unless you
: are doing exotic, experimental code in a field where performance is a
: real issue, just ignore them, like everyone else, and get on with the
: job.

Everyone ignores STL in production code? This is news to me. Wouldn't
it be better to simply limit yourself to a subset of methods and
algorithms, you are comfortable with, and which can be implemented to be
safe according to your standards, yet still conforming.

:     [Excellent comments on the standard cut...]

: Oleg, why weren't you on the committee?

Up until recently I was a graduate student in math, making too little
money to pay for my own participation. I don't think the math
department would be too amused at my proposition to pay for my
participation.  Besides, until very recently I had nothing to
contribute, since I didn't know anything about C++ worth
mentioning. You know, was busy proving theorems and stuff. Now I make
enough money to fully finance my own participation, even if my current
employer won't, but I guess it's too late now. Does committee still
exist or it is dissolved for all practical purposes? Can one still
join the committee? Would it make any sense? I guess there still will
be defect reports and stuff.  Or a new committee will be formed for
that?

: My opinion is that most of the standard library is unusable in
: production code.  But I don't blame the committee; given the historical
: conditions, it is hard to see what they could have done differently.
: Good solid criticism like yours, much earlier, might have resulted in a
: library that was usable.  A good solid proposal for the necessary
: container classes, much, much earlier, certainly would have resulted in
: a much better library.  The library that was adopted WAS the best
: library proposed.  In fact, it was the only library proposed.

Well, it's a miracle it was adopted at all. I think it was a good
thing. It definitely showed me the light, and concentrated much larger
resourses to develop it and understand the paradigm, then otherwize
would have. I think it was one of the best things that happened to
C++. If I am not satisfied with STL, I can always write my own
containers, now that I see the light.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/29
Raw View
Andrew Koenig <ark@research.att.com> wrote:
: In article <69m848$c11@marianna.psu.edu>,
: Oleg Zabluda  <zabluda@math.psu.edu> wrote:

: > struct input_iterator_tag {};
: > struct forward_iterator_tag : public const input_iterator_tag {};

: > Why not just put a virtual destructor in the input_iterator_tag?
: > Who are we kidding anyway? It's not like input_iterator_tag or
: > forward_iterator_tag have zero size or anything. Besides, normally
: > we would never instantiate either of them. It's there simply to use
: > the overload resolution mechanism for compile time binding.

: Exactly.  And the way one uses the overload resolution mechanism
: is to by passing an object of that type as an argument.

: Now, regardless of whether or not the object has zero size, if it
: has no data that need to be copied, it takes a much less intelligent
: compiler to optimize away the copying than it does if there is a
: destructor (and a virtual one, no less!) to worry about.

: So the extra mechanism would require a significant amount of
: justification before the committee would have been convinced it
: was worthwhile.  As it happens, no one even attempted that
: justification.

Actually, if you take a look at my original post, which started
this thread, I was actually talking about something which would
make it unnesessary, but it was lost in the flame war. That's what
I wrote:

        > I mean, if even stdlib can't follow elementary good coding
        > practices without a noticeable performance hit, maybe
        > there is something wrong with the language?

What I meant to say is this:

Why on Earth the following is an undefined behaviour:

class A{};
class B : public A {};
A* pa = new B;
delete pa;

While the following moral equivalent is perfectly well defined,
as far as I understand:

class A{};
class B : public A {};
A* pa = new B;
pa -> ~A();
operator delete(pa);

It seems (at least naively) that the former could be defined and
should have the same effect as the later.

And this was indeed the question I really meant to ask, but I guess
hints are not for Usenet.

: > template<class Arg, class Result>
: > struct unary_function {
: > // a bunch of typedef's, but no virtual destructor
: > };

: > template<class T>
: > struct logical_not : public unary_function<T, bool> {
: > // ...
: > };

: > Now, why choose this over a simple

: > template<class T>
: > struct logical_not {

: >  typedef T Arg;
: >  typedef bool Result;

: > };

: Because the former expresses the programmer's intentions much
: more clearly.  It is obvious that logical_not is
: `a unary function from T to bool' in the first example,
: but the second requires careful reading to divind that intention.

That's what comments are for.

Anyway, clarity is obviously a very subjective thing. For example for
me personally, it is clear that logical_not is _NOT_ `a unary function
from T to bool'. At least under the currect C++ rules it isn't. And it
does indeed require careful reading to divind that, because at first
it seems like it _IS_. Even worse, I am required to read not only the
interface of unary_function<>, but the implementation as well to
divind that. Plus I have to divind why the committee didn't put a
virtual destructor there in the first place.

Hey, weren't you the one (together with Matt Austern) who had to
explain the committee's intentions in this respect to some Tessmann
dude, just 3 weeks ago. How come he didn't divind it himself, if it's
so clear and obvious?

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/29
Raw View
Bradd W. Szonye <bradds@concentric.net> wrote:
: Would an implementation that gave these things virtual destructors be
: conforming?

I think not, because whether the destructor is virtual or not
can be observed like this:

class A : public forward_iterator_tag {
 public:
   ~A() { cout << "Destructor is virtual\n"; }
};

forward_iterator_tag* ptr = new A;
ptr -> ~forward_iterator_tag();

As far as I understand, this program is well-formed whether the
destructor is virtual or not, but the output must be different for
the two cases. Therefore you can't just put a virtual destructor,
justifying it by the as-if rule.

: Would then optimizing the actual objects out of the generated
: code be conforming?

I think yes.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David R Tribble <david.tribble@central.beasys.com>
Date: 1998/01/21
Raw View
Matt Austern wrote:
> That begs the question of whether it is always bad style to inherit
> from a class that has no virtual functions.  I know that some people
> think it is; I don't.
>
> Virtual functions are useful if you're planning to use a base class as
> an interface.  (That is, if you're going to access an object via a
> pointer or reference to its base class.)

An example of this is a base class that contains nothing but static
functions; in effect, the class acts like a namespace.  The class
would have no constructors or destructors (or rather they would be
made private and thus inaccessible to clients), since no objects
of that type would ever be created; you use the class only to access
the static methods.

Inheriting from this base class would be done to extend the
implementation, presumably by adding more member functions in the
derived class.  (This is similar to the way Java 'extends' an
implementation class.)  Again, no actual objects of the derived
type would ever be created, so it doesn't matter whether we have a
virtual destructor or not.

-- David R. Tribble, david.tribble@noSPAM.central.beasys.com --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: David Chase <chase@world.std.com>
Date: 1998/01/22
Raw View
David R Tribble wrote:

> An example of this is a base class that contains nothing but static
> functions; in effect, the class acts like a namespace.  The class
> would have no constructors or destructors (or rather they would be
> made private and thus inaccessible to clients), since no objects
> of that type would ever be created; you use the class only to access
> the static methods.

If it acts like a namespace, why not use a namespace?  It is good style
to reduce ambiguity and use the right language feature in the right
place.  (Yes, I know that some compilers may not implement namespaces,
but surely the only reason to mention such compilers here is so that
they may be avoided, RIGHT?)

--
David Chase, chase@world.std.com
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Mark Wilden" <Mark@mWilden.com>
Date: 1998/01/18
Raw View
Nathan Myers wrote in message <69nch9$35c$1@shell7.ba.best.com>...
>> : My personal recommendation is to cut down on caffeine, and wait for
>> : clarity.  Take deep breaths.
>>
>The recommendation above is the best advice I know of.
>I follow it as often as possible.  Whoever can't follow it
>certainly won't be very interested in whatever else I have to offer.

The "recommendation" was patronizing and personal, and has no business
here.

>Before
>making even more demands on our volunteer time, maybe one should
>establish one's competence to define "bad style".  Or not.

Nonsense. No establishment of competence is required (this is the
fallacious "argument from authority"). All that's necessary is to
point out what, in the opinion of the poster, is infelicitous, and the
reasons for that opinion. Neither trotting out of credentials nor
personal remarks about each other's drug use is required.

 [Moderator's note: OK, enough on the debating tactics -- can we please
 get back to talking about C++ and the C++ standard?  -mod (fjh).]

---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1998/01/18
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

|>  If you have a class which is not supposed to be used polymorphically,
|>  why derive from it in the first place? To save some typing? Since when
|>  it's a valid reason to risk an undefined behavior?

Just for the record (and not to reject Oleg's objections), the reason
for inheritance in this particular case is not polymorphic use, but to
support some particularly exoteric forms of type induction.

The standard library, or at least the containers in it, are NOT designed
for derivation, at least not in the classical sense (the isA
relationship).  They are concrete classes (read what Scott Meyers says
about deriving from concrete classes).

|>  Let's review the stuff I was complaining about, shall we:
|>
|>  1.
|>
|>  struct input_iterator_tag {};
|>  struct forward_iterator_tag : public const input_iterator_tag {};
|>
|>  Why not just put a virtual destructor in the input_iterator_tag?
|>  Who are we kidding anyway? It's not like input_iterator_tag or
|>  forward_iterator_tag have zero size or anything. Besides, normally
|>  we would never instantiate either of them. It's there simply to use
|>  the overload resolution mechanism for compile time binding. If some
|>  poor dude somehow manages to instantiate it, we can rely on a good
|>  optimizer or compiler magic to optimize the empty destructors away.
|>  In retrurn we get the situation when if a user's code looks innocent
|>  to someone with no more then one Ph.D, it will probably be legal as
|>  well.

I suspect that the reason is that putting a virtual destructor in them
would cause all of the iterators derived from them to contain a vptr.
In some cases, this would double the size of the iterator.

As for the undefined behavior: there are so many ways to get undefined
behavior from the standard containers that one way more isn't going to
make any real difference.  There are, in fact, so many ways of
accidentally getting undefined behavior from the standard containers
that no reasonable person will use them in production code.  Unless you
are doing exotic, experimental code in a field where performance is a
real issue, just ignore them, like everyone else, and get on with the
job.

    [Excellent comments on the standard cut...]

Oleg, why weren't you on the committee?

My opinion is that most of the standard library is unusable in
production code.  But I don't blame the committee; given the historical
conditions, it is hard to see what they could have done differently.
Good solid criticism like yours, much earlier, might have resulted in a
library that was usable.  A good solid proposal for the necessary
container classes, much, much earlier, certainly would have resulted in
a much better library.  The library that was adopted WAS the best
library proposed.  In fact, it was the only library proposed.

|>  In short, there is never any good reason to derive from a
|>  non-polymorphic class, period. Sometimes there are bad reasons.
|>  It's too bad they creeped into the stdlib. Nothing to say that
|>  the absense of a virtual destructor simply and squarely breaks
|>  the IS_A relationship, commonly known as LSP. And in a pretty
|>  spectacular way too. Very bad.

Agreed.

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/01/19
Raw View
Mark Wilden <Mark@mWilden.com> writes:

> Nathan Myers wrote in message <69nch9$35c$1@shell7.ba.best.com>...
> >> : My personal recommendation is to cut down on caffeine, and wait for
> >> : clarity.  Take deep breaths.

And don't drink coffee often, and I'll try to find a solution
which can satisfy Mark Wilden.

> >Before
> >making even more demands on our volunteer time, maybe one should
> >establish one's competence to define "bad style".  Or not.

Bad style is what I don't like.  ;-)

The general rule can be expressed as:
- polymorphic classes should be a virtual, protected or
  private dtor
- don't derive from a non-polymorphic class

So an option could be to make the iterator<>:

template <...>
class iterator
{
protected:
    iterator () {}
...
};

it doesn't work for xxx_iterator_tag, because you need to
instantiate them directly, but th only way to get undefined
behavior is:

forward_iterator_tag* ptr = new bidirectional_iterator_tag;
delete ptr;

Thus:

class forward_iterator_tag
{
private:
    void operator delete (void*);
};

should do the trick.

Note that I am _not_ seriously proposing the above solution.

There are many ways to get undefined behaviour in programs
which do something with the STL; I don't think that we should
loose time with silly programs which do nothing.

(But I personnaly thinks that the STL is almost perfect, so
I don't consider we should change other undefined behaviours.)

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/19
Raw View
In article <34C2579C.574F@pratique.fr>,
Valentin Bonnard  <bonnardv@pratique.fr> wrote:
>The general rule can be expressed as:
>- polymorphic classes should be a virtual, protected or
>  private dtor
>- don't derive from a non-polymorphic class

Evidently the "general rule" is not general enough, if it dictates
filling up empty base classes with meaningless clutter.

>class forward_iterator_tag { private: void operator delete (void*); };
>should do the trick.

But this makes

  struct MyClass : forward_iterator_tag {};

  MyClass m = ...;
  delete m;

illegal too, even though it's perfectly safe and reasonable to do.  The
right solution is to repair the rule that erroneously claims there is
something wrong with forward_iterator_tag as it is.

The inappropriate application of the rule seems to result from a
confusion of levels; between "Object-Oriented Design" and "Generic
Programming", if you need labels.  The Liskov Substitution Principle
applies to design -- object-oriented design, in particular -- and not
to C++ in general.  Since the STL is not object-oriented, it should
not surprise anyone that different design principles apply.

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ncm@nospam.cantrip.org
Date: 1998/01/19
Raw View
>If you have a class which is not supposed to be used polymorphically,
>why derive from it in the first place? To save some typing? Since when
>it's a valid reason to risk an undefined behavior?

The standard library can't keep you from writing undefined programs.
Not writing undefined programs is your job.

>struct input_iterator_tag {};
>struct forward_iterator_tag : public const input_iterator_tag {};
>
>Why not just put a virtual destructor in the input_iterator_tag?
>Who are we kidding anyway?

Who indeed?  Show me some a function that even takes a
std::input_iterator_tag* argument (never mind deleting it)
and we can cry in our beer together.

>In short, there is never any good reason to derive from a
>non-polymorphic class, period. Sometimes there are bad reasons.
>It's too bad they creeped into the stdlib. Nothing to say that
>the absense of a virtual destructor simply and squarely breaks
>the IS_A relationship, commonly known as LSP. And in a pretty
>spectacular way too. Very bad.

In other words, "Oleg is smarter than everybody on the committee,
and wants everybody to know it."  There are things in the standard
library that might be fixed, but this isn't helping find them.

Followups to alt.flame, I'm tired of explaining this.

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1998/01/19
Raw View
Oleg Zabluda wrote:
>
> Nathan Myers <ncm@nospam.cantrip.org> wrote:
 > : Oleg Zabluda  <zabluda@math.psu.edu> wrote:
 > : >The almost standard if full of places where a class is derived
 > : >from a class with no virtual destructor.
 > : >...
 > : >I mean, if even stdlib can't follow elementary good coding
 > : >practices without a noticeable performance hit, maybe
 > : >there is something wrong with the language?
 >
 > : Oleg, this is a good one, but couldn't it have waited
 > : until April 1?
>
> I am dead serious, man. Do you think it's normal if
> one has to have two Ph.D degrees to properly use stdlib?
>
> If you have a class which is not supposed to be used polymorphically,
> why derive from it in the first place? To save some typing? Since when
> it's a valid reason to risk an undefined behavior?
>
> Let's review the stuff I was complaining about, shall we:
>
> 1.
>
> struct input_iterator_tag {};
> struct forward_iterator_tag : public const input_iterator_tag {};
>
> Why not just put a virtual destructor in the input_iterator_tag?

Because it's not needed to implement the documented behavior of
input_iterator_tag. Even a Ph.D ought to know that you should only uses
classes in ways that their documentation says will work. Slavishly
following "good coding practices" results in junk like this (a uniform
practice in the Windows API):

typedef struct tagPoint
{
int x;
int y;
} POINT, *PPOINT;

Now, in C the typedef is quite useful. I don't happen to like PPOINT,
but that's not the issue. tagPoint, on the other hand, is utterly
useless, and is only there in case someone needs to refer to this struct
inside the definition of the struct. It can be omitted with no loss of
capability.
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/14
Raw View
The almost standard if full of places where a class is derived
from a class with no virtual destructor.

Here is one:

struct input_iterator_tag {};
struct forward_iterator_tag : public const input_iterator_tag {};

[many more like this]

Here is another one:

template<class Arg, class Result>
struct unary_function {
// a bunch of typedef's, but no virtual destructor
};

template<class T>
struct logical_not : public unary_function<T, bool> {
// ...
};

[many more like this]

(24.3.3 par 4) has the following as an example of a good style:

class MyIterator : public iterator<bidirectional_iterator_tag, ... > {
//...
};

And so on.

I mean, if even stdlib can't follow elementary good coding
practices without a noticeable performance hit, maybe
there is something wrong with the language?

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/14
Raw View
Oleg Zabluda  <zabluda@math.psu.edu> wrote:
>The almost standard if full of places where a class is derived
>from a class with no virtual destructor.
>...
>I mean, if even stdlib can't follow elementary good coding
>practices without a noticeable performance hit, maybe
>there is something wrong with the language?

Oleg, this is a good one, but couldn't it have waited
until April 1?

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/14
Raw View
Nathan Myers <ncm@nospam.cantrip.org> wrote:
: Nathan Myers <ncm@nospam.cantrip.org> wrote:
: >Oleg Zabluda  <zabluda@math.psu.edu> wrote:
: >>The almost standard if full of places where a class is derived
: >>from a class with no virtual destructor.
: >>...
: >>I mean, if even stdlib can't follow elementary good coding
: >>practices without a noticeable performance hit, maybe
: >>there is something wrong with the language?
: >
: >Oleg, this is a good one, but couldn't it have waited
: >until April 1?

: John asked me to clarify.

: The reference to April 1 refers to "April Fools' Day", not
: a committee deadline.

: We on the standard committee felt that there was no need to mention
: the destructors for empty base classes because they are not actually
: objects.

Sure they are objects. Sometimes complete, sometimes incomplete.
But perfectly good objects nevertheless. What's your definition
of an ``actually object''?

: Only an object needs a destructor, virtual or otherwise.

Incorrect.

forward_iterator_tag* ptr = new bidirectional_iterator_tag;
delete ptr;

Undefined behavior. Doesn't matter if the classes are empty
or whatever.

: In general, a virtual destructor is appropriate only when one expects
: pointers to the base class to be passed as arguments.  In the cases
: Oleg cites, such arguments would be completely useless, as there are
: no virtual function members to make such an argument useful.

I'll take a useless program over the one with an undefined behavior
any time.

: Good coding practices don't include adding meaningless but confusing
: clutter, especially if it also threatens user components' performance.

Not putting a virtual destructor there is meanigless. What threats
to performance? Just how many instances of, say, forward_iterator_tag
do you expect in a ``meanigful'' program? I expect exactly zero.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/15
Raw View
Nathan Myers <ncm@nospam.cantrip.org> wrote:
>Oleg Zabluda  <zabluda@math.psu.edu> wrote:
>>The almost standard if full of places where a class is derived
>>from a class with no virtual destructor.
>>...
>>I mean, if even stdlib can't follow elementary good coding
>>practices without a noticeable performance hit, maybe
>>there is something wrong with the language?
>
>Oleg, this is a good one, but couldn't it have waited
>until April 1?

John asked me to clarify.

The reference to April 1 refers to "April Fools' Day", not
a committee deadline.

We on the standard committee felt that there was no need to mention
the destructors for empty base classes because they are not actually
objects.  Only an object needs a destructor, virtual or otherwise.

In general, a virtual destructor is appropriate only when one expects
pointers to the base class to be passed as arguments.  In the cases
Oleg cites, such arguments would be completely useless, as there are
no virtual function members to make such an argument useful.

Good coding practices don't include adding meaningless but confusing
clutter, especially if it also threatens user components' performance.

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: David Chase <chase@world.std.com>
Date: 1998/01/15
Raw View
Nathan Myers wrote:

> We on the standard committee felt that there was no need to mention
> the destructors for empty base classes because they are not actually
> objects.  Only an object needs a destructor, virtual or otherwise.

Sounds like you haven't spent much time using (or developing)
those automated style-checking and/or runtime-instrumenting
tools for C++.  One of the more annoying problems in getting
one of those ready to ship is configuring it not to whine about
all the bad style, leaks, and even bugs in the platform
software.  It is, of course, a simple matter of programming
to enumerate all the classes and headers that don't conform to
an automated idea of what style should be, but it only adds
more hair (and sometimes confusion -- "how come you complain
about this in my code, but not in the headers?") to an already
hairy product.

> Good coding practices don't include adding meaningless but confusing
> clutter, especially if it also threatens user components' performance.

Good coding practices are consistently used.  If it threatens
performance, bitch at the compiler writers to improve it.
Besides, hardware is getting faster more quickly than software
is getting correct; it seems like we should do everything we
can to smooth the road towards "correct", and let the hardware
guys worry about "faster" for a while.

--
David Chase, chase@world.std.com
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/15
Raw View
In article <69kejv$9sa@marianna.psu.edu>,
Oleg Zabluda  <zabluda@math.psu.edu> wrote:
>Nathan Myers <ncm@nospam.cantrip.org> wrote:
>: Nathan Myers <ncm@nospam.cantrip.org> wrote:
>: >Oleg Zabluda  <zabluda@math.psu.edu> wrote:
>: >>The almost standard if full of places where a class is derived
>: >>from a class with no virtual destructor.
>: >>...
>: >>I mean, if even stdlib can't follow elementary good coding
>: >>practices without a noticeable performance hit, maybe
>: >>there is something wrong with the language?
>: >
>: >Oleg, this is a good one, but couldn't it have waited
>: >until April 1?
>
> What's your definition of an ``actually object''?

There are more than enough reference works on the subject.
I promise not to add to the problem.

>: Only an object needs a destructor, virtual or otherwise.
>
>Incorrect.
>
>forward_iterator_tag* ptr = new bidirectional_iterator_tag;
>delete ptr;
>
>Undefined behavior. Doesn't matter if the classes are empty
>or whatever.

"Doctor, doctor, it hurts when I do *this*."
"Then don't *do* that."

>: In general, a virtual destructor is appropriate only when one expects
>: pointers to the base class to be passed as arguments.  In the cases
>: Oleg cites, such arguments would be completely useless, as there are
>: no virtual function members to make such an argument useful.
>
>I'll take a useless program over the one with an undefined behavior
>any time.

I'm happy to do without both, thank you.

>: Good coding practices don't include adding meaningless but confusing
>: clutter, especially if it also threatens user components' performance.
>
>Not putting a virtual destructor there is meaningless. What threats
>to performance? Just how many instances of, say, forward_iterator_tag
>do you expect in a ``meanigful'' program? I expect exactly zero.

My personal recommendation is to cut down on caffeine, and wait for
clarity.  Take deep breaths.

Nathan Myers
ncm@nospam.cantrip.org   http://www.cantrip.org/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/15
Raw View
David Chase  <chase@world.std.com> wrote:
>Nathan Myers wrote:
>> We on the standard committee felt that there was no need to mention
>> the destructors for empty base classes because they are not actually
>> objects.  Only an object needs a destructor, virtual or otherwise.
>
>Sounds like you haven't spent much time using (or developing)
>those automated style-checking and/or runtime-instrumenting
>tools for C++.

Guilty as charged, and for a long time to come I hope.

>  It is, of course, a simple matter of programming
>to enumerate all the classes and headers that don't conform to
>an automated idea of what style should be ...

It's a simple matter of good sense and experience to come up
with reasonable rules.  If your tools' rules are too simplistic,
demand better (or more programmable) tools; that's what you're
paying for.  Demanding more language complexity because your
Procrustean checking machine can't understand it as it is makes
little sense.

>> Good coding practices don't include adding meaningless but confusing
>> clutter, especially if it also threatens user components' performance.

> Good coding practices are consistently used.

Good coding practices are "good" first and "consistently used" second.
Just because somebody makes up a rule doesn't make it a good one.

>If it threatens performance, bitch at the compiler writers to improve it.

I like it fine the way it is: virtual functions have a simple,
predictable cost.  Optimizing out abstraction is what I pester
compiler people for.  Optimizing out meaningless clutter that
shouldn't be there in the first place is a distant Nth on my
priority list.

>Besides, hardware is getting faster more quickly than software
>is getting correct; it seems like we should do everything we
>can to smooth the road towards "correct", and let the hardware
>guys worry about "faster" for a while.

I can use all the performance the hardware guys can provide.
As a former hardware guy, I can tell you that there's nothing
more frustrating than building something fast and then seeing
programmers slow it down because they can't be bothered to
understand it -- and then complaining.

Just out of curiosity... whose definition of "correct" do you
think we should be shooting for?  It's easy to take potshots at
the Standard because it hides nothing and doesn't shoot back.
(Some people even manage to make a career of it.  Nice work if
you can get it, and don't care about actually helping anybody.)

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/15
Raw View
Nathan Myers <ncm@nospam.cantrip.org> wrote:
: In article <69kejv$9sa@marianna.psu.edu>,
: Oleg Zabluda  <zabluda@math.psu.edu> wrote:
: >Nathan Myers <ncm@nospam.cantrip.org> wrote:
: >: Nathan Myers <ncm@nospam.cantrip.org> wrote:
: >: >Oleg Zabluda  <zabluda@math.psu.edu> wrote:
: >: >>The almost standard if full of places where a class is derived
: >: >>from a class with no virtual destructor.
: >: >>...
: >: >>I mean, if even stdlib can't follow elementary good coding
: >: >>practices without a noticeable performance hit, maybe
: >: >>there is something wrong with the language?
: >: >
: >: >Oleg, this is a good one, but couldn't it have waited
: >: >until April 1?
: >
: > What's your definition of an ``actually object''?

: There are more than enough reference works on the subject.
: I promise not to add to the problem.

Well, I will. Everybody who has a problem with the definition of
an ``actually object'', should consult 1.7 "The C++ object model".

: >: Only an object needs a destructor, virtual or otherwise.
: >
: >Incorrect.
: >
: >forward_iterator_tag* ptr = new bidirectional_iterator_tag;
: >delete ptr;
: >
: >Undefined behavior. Doesn't matter if the classes are empty
: >or whatever.

: "Doctor, doctor, it hurts when I do *this*."
: "Then don't *do* that."

<yawn>

[...]

: >: Good coding practices don't include adding meaningless but confusing
: >: clutter, especially if it also threatens user components' performance.
: >
: >Not putting a virtual destructor there is meaningless. What threats
: >to performance? Just how many instances of, say, forward_iterator_tag
: >do you expect in a ``meanigful'' program? I expect exactly zero.

: My personal recommendation is to cut down on caffeine, and wait for
: clarity.  Take deep breaths.

I don't think you should escalate this to a flame war. You'll lose.

Lighten up, man. Nobody doubts your manhood just because threre
is a bad style in the part of the library you didn't even write.
I am too scared of the part you've written, so you are safe for now.
Drop the attitude and simply explain why the bad style was chosen
over alternatives, of which there are plenty, or why the language rules
absolutely can't allow the code above, which currently is an undefined
behaviour. That would be quite enough.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/01/16
Raw View
Nathan Myers <ncm@nospam.cantrip.org> wrote:
: Oleg Zabluda  <zabluda@math.psu.edu> wrote:
: >The almost standard if full of places where a class is derived
: >from a class with no virtual destructor.
: >...
: >I mean, if even stdlib can't follow elementary good coding
: >practices without a noticeable performance hit, maybe
: >there is something wrong with the language?

: Oleg, this is a good one, but couldn't it have waited
: until April 1?

I am dead serious, man. Do you think it's normal if
one has to have two Ph.D degrees to properly use stdlib?

If you have a class which is not supposed to be used polymorphically,
why derive from it in the first place? To save some typing? Since when
it's a valid reason to risk an undefined behavior?

Let's review the stuff I was complaining about, shall we:

1.

struct input_iterator_tag {};
struct forward_iterator_tag : public const input_iterator_tag {};

Why not just put a virtual destructor in the input_iterator_tag?
Who are we kidding anyway? It's not like input_iterator_tag or
forward_iterator_tag have zero size or anything. Besides, normally
we would never instantiate either of them. It's there simply to use
the overload resolution mechanism for compile time binding. If some
poor dude somehow manages to instantiate it, we can rely on a good
optimizer or compiler magic to optimize the empty destructors away.
In retrurn we get the situation when if a user's code looks innocent
to someone with no more then one Ph.D, it will probably be legal as
well.

2.

template<class Arg, class Result>
struct unary_function {
// a bunch of typedef's, but no virtual destructor
};

template<class T>
struct logical_not : public unary_function<T, bool> {
// ...
};

Now, why choose this over a simple

template<class T>
struct logical_not {

 typedef T Arg;
 typedef bool Result;

}:

It's not like passing logical_not by a pointer/reference to
unary_function<T, bool> would be very smart, would it? You know,
no virtual functions and stuff. So why not just disallow it, by not
deriving in the first place? Besides, the second variant is
clear to even die-hard GP fanatics boycotting OOP.

3.

class MyIterator : public iterator<bidirectional_iterator_tag, ... > {
//...
};

That's totally unnesessary too. Again you are not going to
use MyIterator polymorphically. Specializing some traits class
would be a much better idea:

template<class T> struct iterator_info;
template<> struct iterator_info<MyIterator> {
   typedef bidirectional_iterator_tag  iterator_category;
   //....
};

Then instead of MyIterator::iterator_category you say
iterator_info<MyIterator>::iterator_category.


Yet another way is to just say what you mean. If you want to use
static polymorphism, (I hate this expression) just say so:

template<class Iter_info>
class basic_MyIterator {
typedef Iter_info::iterator_category  iterator_category;
//...
};

typedef
  basic_MyIterator< iterator<bidirectional_iterator_tag, ... > >
  MyIterator;


Alternatively, you can derive iterator_info<MyIterator> from
iterator<> (after adding a virtual destructor to
iterator<>, of course). This (addition) is harmless, since
normally nobody would instantiate iterator_info<MyIterator>'s.


In short, there is never any good reason to derive from a
non-polymorphic class, period. Sometimes there are bad reasons.
It's too bad they creeped into the stdlib. Nothing to say that
the absense of a virtual destructor simply and squarely breaks
the IS_A relationship, commonly known as LSP. And in a pretty
spectacular way too. Very bad.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Xavier Huet <xavier@saucisson.a.lail.com>
Date: 1998/01/16
Raw View
Nathan Myers wrote:

> Nathan Myers <ncm@nospam.cantrip.org> wrote:
> >Oleg Zabluda  <zabluda@math.psu.edu> wrote:
> >>The almost standard if full of places where a class is derived
> >>from a class with no virtual destructor.
> >>...
> >>I mean, if even stdlib can't follow elementary good coding
> >>practices without a noticeable performance hit, maybe
> >>there is something wrong with the language?
> >
> >Oleg, this is a good one, but couldn't it have waited
> >until April 1?
>
> John asked me to clarify.
>
> The reference to April 1 refers to "April Fools' Day", not
> a committee deadline.
>
> We on the standard committee felt that there was no need to mention
> the destructors for empty base classes because they are not actually
> objects.  Only an object needs a destructor, virtual or otherwise.
>
> In general, a virtual destructor is appropriate only when one expects
> pointers to the base class to be passed as arguments.  In the cases
> Oleg cites, such arguments would be completely useless, as there are
> no virtual function members to make such an argument useful.
>
> Good coding practices don't include adding meaningless but confusing
> clutter, especially if it also threatens user components' performance.
>

I guess I am lost.
1) What does that mean when you write "empty base classes because they're
are not actually objects" ?
   Do you mean that they are Abstract Base Class or that they have no useful
   functions ?
   They are not Abstract since they do not have any pure function (they do not
   have functions though).
   They have no functions but they hold the "typedef"s.

2) "In general, a virtual destructor is appropiate only when one expects pointers

    to the base class to be passed as arguments"
    IMHO, it's not the reason to have a virtual destructor. We want to have
virtual
    destructor when we delete the object thru one of this object's base class
pointer.

To conclude, I am wondering since the STL function use base class but with not
base class semantic, is there another way to achieve the same without a base
class.


~Xavier
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Matt Austern <austern@isolde.mti.sgi.com>
Date: 1998/01/16
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

> Lighten up, man. Nobody doubts your manhood just because threre
> is a bad style in the part of the library you didn't even write.
> I am too scared of the part you've written, so you are safe for now.
> Drop the attitude and simply explain why the bad style was chosen
> over alternatives, of which there are plenty, or why the language rules
> absolutely can't allow the code above, which currently is an undefined
> behaviour. That would be quite enough.

That begs the question of whether it is always bad style to inherit
from a class that has no virtual functions.  I know that some people
think it is; I don't.

Virtual functions are useful if you're planning to use a base class as
an interface.  (That is, if you're going to access an object via a
pointer or reference to its base class.)  There are circumstances,
though, where you want to use inheritance for some reason that has
nothing at all to do with using a base class as an interface.  This is
one of them.  The iterator and function object base classes are purely
an implementation detail; they are a (slight) convenience when you're
defining new components.

Fundamentaly, the straightforward answer to Oleg's question is that
that section of the standard was written by someone whose opinions
about style are closer to mine than to his.

(But personally, if I were defining the STL from scratch, I'd probably
not bother with those base classes at all.  They don't hurt anyone,
but they aren't all that much of a convenience.  And they have managed
to mislead some people into thinking that inheritance is somehow
fundamental to the design of the STL.)


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1998/01/16
Raw View
[substantially edited for clarity]

>Nathan Myers <ncm@nospam.cantrip.org> wrote:
>: "Doctor, doctor, it hurts when I do *this*."
>: "Then don't *do* that."

Oleg Zabluda wrote in message <69linc$crm@r02n01.cac.psu.edu>...
><yawn>


>[...] simply explain why the bad style was chosen
>over alternatives, of which there are plenty, or why the language rules
>absolutely can't allow

[forward_iterator_tag* ptr = new bidirectional_iterator_tag;]
[delete ptr;]

> which currently is an undefined
>behaviour. That would be quite enough.

I can think of one good reason to use this bad style, and it's the reason
given: it's easy to implement and optimize, and the "bad" result isn't
intended to be used. Therefore, I think the "don't do that" remark is
appropriate.

The easy optimization: allocate minimal, uninitialized stack space for the
"tags" on the stack when passing as a parameter. The better, more general
optimization: don't allocate the stack space at all, even if it *has* a
virtual destructor, since the object is never used and has no side effects
whatsoever (that is allowed, isn't it?).

The intended use is as a type, not as an object. It's obvious that you'd
never want to heap-allocate these things, because it has no virtual
destructor, and because of the examples of use in the library already. If
that's not immediately obvious to naive users, they learn quickly enough;
preferably from primers that explain "don't use these things at all" and
from more advanced texts that explain "use these only as 'auto' arguments to
functions for algorithm specialization."

Would an implementation that gave these things virtual destructors be
conforming? Would then optimizing the actual objects out of the generated
code be conforming? If the answers are "yes" and "yes" then there's no
problem at all with this bad style. If either answer is "no" then the only
problem is that it's bad style. And maybe not even then; I personally just
took it as a new idiom, like traits (which aren't ever instantiated at all).
These things don't belong on the heap.

The implementation of 'offsetof' or 'va_start' is inherently non-portable
and typically exposed to users, and therefore bad style too. However, it's
necessary to make the things work the way you expect.
---
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds



[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: David Chase <chase@world.std.com>
Date: 1998/01/16
Raw View
Nathan Myers wrote:
> It's a simple matter of good sense and experience to come up
> with reasonable rules.

It's not as easy as you think if those rules are to be expressed
in an automated form, and if they are to interact well with
typical programming practice, typical programmers, and suppressions.

> ... If your tools' rules are too simplistic,
> demand better (or more programmable) tools; that's what you're
> paying for.

> ... Demanding more language complexity because your
> Procrustean checking machine can't understand it as it is makes
> little sense.

Note the difference in our views here -- I say that correctness
is important, that it would be nice if the language were biased
a little more in the direction of accidentally getting programs
right instead of accidentally getting them wrong, and that if
there are runtime costs, demand a better optimizer.  You seem to
be saying that performance wins here, and that if it confuses
the tools, demand better checking tools.  My point is that, given
good compilers, all the tweaks and frobs for performance in C++
do not buy you more than one year's performance advantage (i.e.,
less than a factor of 1.6, and I am being very generous to C++
here) over other languages that contain fewer knobs designed
to allow a rocket surgeon to produce brilliantly fast code.
(Note that most programmers are not rocket surgeons anyway.)

> >Besides, hardware is getting faster more quickly than software
> >is getting correct; it seems like we should do everything we
> >can to smooth the road towards "correct", and let the hardware
> >guys worry about "faster" for a while.

> Just out of curiosity... whose definition of "correct" do you
> think we should be shooting for?  It's easy to take potshots at
> the Standard because it hides nothing and doesn't shoot back.

I don't mean correctness of the standard, I mean correctness
of programs written in C++.  It's a complicated language, and
it comes with many of the defaults "set wrong" (for various
reasons that have been beaten to death before), which can
often be a source of bugs.  The style checkers are one way
to help reduce this problem, in that they can point out places
where you may have made a mistake, and they do avoid warning
about style violations in libraries.

Unfortunately, the style checkers are only approximate both
ways; they certainly don't find all bugs, and many of the things
that they whine about are perfectly correct in certain contexts.
A typical first reaction to diagnostics from one of these tools
is "don't tell me how to write code, you stupid computer, I
know what I'm doing."  That's often the wrong reaction, but
remember, these are customers, and they are "always right", no
matter how wrong they really are.  If the style rule is not
usually right, or not very easy to explain when it is wrong,
then it has to be left out.

At times, the suppressions can interact with the rest of the
product, if a suppressed warning generates a later cascade of
less obvious errors.  A large part of the design of these
products involves predicting how people will react to some
of the advisories; it is relatively easy to detect that there
is *some* error and simple say "error detected".  If the
diagnostic does not connect the automated detection of an
error or a style violation to the bug as the programmer
understands it, the programmer may simply ignore the warning
("I know what I'm doing, I must have confused the stupid tool.")
Or, if the tool cries wolf too often, a user will simply not
believe it when it does report an error.  If a user does
suppress incorrectly, how is a subsequent error or
diagnostic reported?  Can it point back to the root
cause?  (And what sort of a performance penalty
for the checking tool do you think is acceptable?)

So, though there are tools for detecting style violations,
they are far from perfect, and the more hair there is in
the language, the harder it is to make them be useful.

--
David Chase, chase@world.std.com


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/01/17
Raw View
Oleg Zabluda  <zabluda@math.psu.edu> wrote:
> Nathan wrote:
> : My personal recommendation is to cut down on caffeine, and wait for
> : clarity.  Take deep breaths.
>
> I don't think you should escalate this to a flame war. You'll lose.

Everybody loses in a flame war.

The recommendation above is the best advice I know of.
I follow it as often as possible.  Whoever can't follow it
certainly won't be very interested in whatever else I have to offer.

> Lighten up, man. Nobody doubts your manhood just because threre
> is a bad style in the part of the library you didn't even write.
> I am too scared of the part you've written, so you are safe for now.

It amuses me that to think that somebody claims to know which parts of
the standard library I wrote.

> Drop the attitude and simply explain why the bad style was chosen
> over alternatives, of which there are plenty, or why the language rules
> absolutely can't allow the code above, which currently is an undefined
> behaviour. That would be quite enough.

It's Standard.  Those of us who volunteered our time making the
Standard liked it that way, unanimously, and still do.  Before
making even more demands on our volunteer time, maybe one should
establish one's competence to define "bad style".  Or not.

Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]