ISO/ IEC JTC1/SC22/WG21 N0736


Accredited Standards Committee X3       Doc No: X3J16/95-0136R1 WG21/N0736R1
Information Processing Systems          Date:    Jul 28, 1995
Operating under the procedures of       Project: Programming Language C++
American National Standards Institute   Ref Doc:
                                        Reply to: Josee Lajoie
                                                  (josee@vnet.ibm.com)


ANSI Public Review Comments (Revision 1)
===========================

The ANSI public review comment period ended on July 26th, 1995.
These are the comments I had received at the time the mailing deadline
arrived on July 28th 1995. Unless some comments arrived late at ANSI,
this is probably the entire list of ANSI public comments.

    Statistics:
      23 core language
      21 library
      17 requests for language extensions
      1 templates
      1 syntax
      1 environment

    Distributions:
      1. Syntax
      2. Extensions
      3. Extensions
      4. Extensions
      5.1 Core
      5.2 Library
      5.3 Core
      5.4 Extensions
      5.5 Library (Extensions)
      5.6 Extensions
      6. Library
      7.
      8.1 Editorial
      8.2 Core
      8.3 Core
      8.4 Templates
      8.5 Extensions
      8.6 Library
      8.7 Library
      9.1 Core
      9.2 Core
      9.3 Core
      9.4 Editorial
      9.5 Extensions
      9.6 Core
      T10 Environment
      T11 Library
      T12 Extensions
      T13.1 Core
      T13.2 Core
      T14.1 Extensions / C compatibility
      T14.2 Extensions
      T15.1 Extensions
      T15.2 Extensions
      T16.1 Core
      T16.2 Core
      T16.3 Core
      T16.4 Core
      T17 Extensions / Library
      T18 Template / Extensions
      T19.1 - T19.3 Core
      T19.4 - T19.13 Library
      T20 Extensions
      T21.1 Core
      T21.2 Library
      T22 Library
      T23 Core
      T24 Library
      T25 Core
      T26.1 Core
      T26.2 Library
      T27 Extensions
      T28 Core
      T29 Library
      T30 Library
      T31 Core
      T32 Core
      T33 Library
      T34 Extensions


  =========================================================================

  Public Review Comment #1

    Syntax

  _____________________________________________________________________________

  =========================================================================

  Public Review Comment #2

    Extensions

  _____________________________________________________________________________
  To: X3SEC
  From: EDP on Tue, May 23, 1995 2:26 PM
        Mr. Stephen Bard
  Subject: suggestion for enhancement


  X3 Secretariat
  Attn: Deborah J. Donovan
  1250 Eye St. NW, Suite 200
  Washington, DC 20005

  Dear Ms. Donovan:

  i suggest an addition to the try -- catch mechanism. this would be the
  addition of an optional "clean" clause. so the code would then look like:

  try
  {
  	...
  }
  clean
  {
  	...
  }
  catch ()
  {
  	...
  }

  the idea is that the code in the optional "clean" clause would be
  executed whether or not an exception was thrown. it would be executed
  before the code in the "catch" clause if the exception is thrown. an
  example of where this is helpful is where the method performs a new
  which allocates memory to be used by the code which may throw an
  exception. for example:

     // allocate arrays of file names
     PCSTR *rgpszUpdates = new PCSTR[nCount];
     CString *rgcstrUpdates = new CString[nCount];

     ...

     // call the main method with the arrayed filenames
     BOOL bRet;
     try
     {
        // may throw an exception
        bRet = fInitialize(pszWheelName, pszCoreTitle, rgpszUpdates, nCount);
     }
     clean
     {
        // delete the allocated arrays
        delete [] rgpszUpdates;
        delete [] rgcstrUpdates;
     }
     catch (...)
     {
        // handle exception
        ...
     }

  ------------------------thanks: Stephen Bard--------------

  =========================================================================

  Public Review Comment #3

    Extensions

  _____________________________________________________________________________
  To: X3SEC
  From: Bryant Harris/Edge Research on Wed, May 24, 1995 12:23 PM
  Subject: C++ specifications (implied function calls on class members)

  An extension I would like to see added to C++ is some mechanism for
  implied
  member functions. It is easiest to see the benefits of this with a small
  example.

  class Point {
  protected:
    long x, y;

  public:
    long GetX() const { return x; };
    long GetY() const { return y; };
    void SetX(long nNewX) { x = nNewX; };
    void SetY(long nNewY) { y = nNewY; };
  };

  With implied function calling, I would like to be able to use the above class

  as follows.
  ...
  Point   P;
  P.x = 5;                  // implied function would be equivalent to

  P.SetX( 5 );
  P.y = P.x + 1;       // implied function would be equivalent to
  P.SetY( P.GetX() + 1);
  ....

  Notice, the above is currently illegal as x and y are protected members.
  What
  I am suggesting is that the above example should be legal, and that
  references
  to the protected members implies calls to the appropriate Set?? and Get??
  member functions.

  So whats wrong with using the methods specified?

  If we were implementing some mathmatical function it would be much
  nicer and
  readable to say
    C.x = A.x / B.y + A.y / B.x;
    C.y = A.x / B.y - A.y / B.x;

  than

    C.SetX( A.GetX() / B.GetY() + A.GetY() / B.GetX() );
    C.SetY( A.GetX() / B.GetY() - A.GetY() / B.GetX() );

  So why not make x and y public?

  Well lets say that these points are going to be divided frequently, and we
  would like to preserve some additional fractional precision so that a series
  of
  divides, adds, etc... are more accurate, but that we would like this to be
  transparent to the user of the class.  We could simply make changes to the
  member functions as so:
  ...
    long GetX() const { return x >> 2; };
    long GetY() const { return y >> 2; };
    void SetX(long nNewX) { x = nNewX << 2; };
    void SetY(long nNewY) { y = nNewY << 2; };
  ...

  We are now storing fractions to the nearest 1/4 internally (which if our
  numbers are small may be desirable).  With the internal format of the
  number
  completely hidden from the user of the class,  we can implement storage
  mechanisms like this.  If the members are public, there is a chance the
  user
  will directly modify the members, potentially screwing up the values.   Put
  simply, implied function calls give the ease of use that 'public members'
  provide, while giving the protection that 'public access methods to
  protected
  members' provides.  I have no particular religion on how the syntax for
  declaring an implied function should look, but heres a suggestion.

  class Point {
  protected:
    long x, y;

  public:
    long operatorx() const;                    // GetX
    long operatory() const;                   // GetY
    void operatorx=(long nNewX);     // SetX
    void operatory=(long nNewY);    // SetY
  };

  Or maybe the introduction of a new key word such as 'imply' ( e.g.   imply
  long
  x() const;).  Again, I think the functionality of implied functions is much
  more important than the declaration.

  Note, I believe with some clever programming you could currently get
  something
  like
  Example C
  ...
  Point P;
  P.x() = 5;
  P.y() = P.x() + 1;
  ...

  but this would require that the variables x,y be renamed, and you would
  still
  have those unsightly parenthesis.

  Any questions or comments are welcome.

  Bryant Harris
  Edge Research
  1 Harbour Place Suite # 553
  Portsmouth, NH 03801

  =========================================================================

  Public Review Comment #4

    Extensions

  _____________________________________________________________________________
  To: X3SEC
  From: Peter Durham (Exchange) on Wed, May 31, 1995 6:10 PM
  Subject: ISO/IEC CD 14882 5.17 comment: Deferred assignment operator
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;31 May 1995 17:57:55
  -0500
  Received:  by netmail2.microsoft.com (5.65/25-eef)
  	id AA25744; Wed, 31 May 95 14:36:42 -0700
  Received: by netmail2 using fxenixd 1.0 Wed, 31 May 95 14:36:42 PDT
  Received: from [157.55.18.120] by itgb1b.microsoft.sfs with SMTP
  (5.65/25-eef)
  	id AA02694; Wed, 31 May 95 14:07:41 -0700
  Received: by exchange1 with Microsoft Mail
  	id <01BA2F59.40629FB0@exchange1>; Wed, 31 May 1995 13:20:08 -0700
  Message-Id: <01BA2F59.40629FB0@exchange1>
  From: "Peter Durham (Exchange)" <peterdur@microsoft.com>
  To: "'x3sec@itic.nw.dc.us'" <x3sec@itic.nw.dc.us>
  Subject: ISO/IEC CD 14882 5.17 comment: Deferred assignment operator
  Date: Wed, 31 May 1995 13:05:37 -0700
  Encoding: 21 TEXT

  I have the following suggestion for an addition to the proposed ISO/IEC CD
  14882 or a future C/C++ standard. This comment is relevant to 5.17
  [expr.ass].
  I would like to see a deferred assignment operator defined in the language.
  This operator (for which I suggest the token ,= 'comma-equals') would have
  the following behavior. E1 ,= E2 would assign the value E2 to E1, but the
  value of the expression would be the old value of E1. This should be simple
  for implementors of the language, has the potential of creating more
  efficient code, and makes for some very convenient expressions. For
  example: "a = b ,= a" swaps the values of a and b. "free(px ,= NULL)" would
  free the memory to which px points and set px to NULL.
  I admit that this probably is a little too strange an idea to be added to
  what is now a well-established language, and probably appeals a little too
  much to old-style C obfuscatory coding. On the other hand, I've seen many
  places where having it would have eliminated some awkward coding (like the
  examples above). I hope that this idea provokes some thought, or at least
  amusement.
  - Peter Durham (peterdur@microsoft.com)
  Writing as an individual with personal opinions, not representing my
  employer.

  =========================================================================

  Public Review Comment #5

    5.1 Core issue (Steve Adamczyk)
    5.2 Library issue (Mike Vilot)
    5.3 Core issue (Steve Adamczyk)
    5.4 Request for an extension (Bjarne Stroustrup)
    5.5 Request for a new class library (Mike Vilot)
    5.6 Library (Mike Vilot)

  _____________________________________________________________________________
  To: X3SEC
  From: ATAYLOR@spar.ca on Thu, Jun 1, 1995 9:43 AM
  Subject: Comments for ISO/IEC CD 14882 Public Comments Period - C++
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;1 Jun 1995 09:43:01
  -0500
  Received: by janus.spar.ca id <29477>; Thu, 1 Jun 1995 09:44:46 -0400
  X-Mailer: WordPerfect Office 4.0
  Date: Thu, 1 Jun 1995 09:41:54 -0400
  From: ATAYLOR@spar.ca
  To: x3sec@itic.nw.dc.us
  Subject:  Comments for ISO/IEC CD 14882 Public Comments Period - C++
  Message-Id: <95Jun1.094446edt.29477@janus.spar.ca>

  A hardcopy of this letter has been mailed to you and a copy has been
  mailed to ANSI...

  Please accept the following comments for the Public Comment Period of
  ISO/IEC CD 14882, Programming Language C++.

  5.1. delete and arrays

  Comment:  The syntax "delete x" should recognize whether or not it is
  deleting an array and call operator delete() or operator delete[]()
  appropriately.  This would require storing information in the heap to
  identify
  whether an allocated block is an array.

  Rationale:  It is possible for a programmer to use delete x instead of delete
  [] x on a non-array object, causing undesirable effects.  It is suggested
  that
  the delete [] x syntax be accepted as an anachronism.

  5.2. Global delete should zero its pointer argument

  Comment:  The global delete operator should zero the pointer passed as an
  argument, thus allowing future attempts to delete via that pointer benign.
  Note that this would change the function declarations of the delete
  operators from void operator delete(void *) to void operator delete(void *&)
  and from void operator delete[](void *) to void operator delete[](void *&).

  Rationale:  Pointers are often used to hold dynamic information inside a
  class object.  Success in checking whether the pointer is in use (i.e.,
  pointing to a dynamic object) is usually ensured by setting the pointer to
  zero when it is not in use.  Constructors can be responsible for initializing
  a
  pointer appropriately upon object creation, but the following code is usually
  necessary to support the transition from in use to not in use during the life
  of an object.

                 delete p;    // p is now invalid, but not zero
                 p = 0;

  Making the delete operator zero the pointer would eliminate the necessity
  for the second line of code and would thwart errors caused by a
  programmer who forgets to include it.

  5.3. Meaning of for statement

  Comment:  The for statement

                 for (init-stmt cond-exp; iter-exp) stmt

  should translate to

                 {    // note enclosing braces
                   init-stmt
                   while (cond-exp)
                   {
                     stmt
                     iter-exp;
                   }
                 }

  Rationale:  This would make, for example, multiple uses of the following
  statement fragment possible, allowing for more consistent and maintainable
  code.

                 for (int i; ...

  The translation documented in the Working Papers (6.5.3, page 6-4) would
  cause the second and subsequent occurrences of this statement fragment
  to generate duplicate declaration errors on the object i.

  5.4. Arbitrary precision type or Binary Coded Decimal type

  Comment:  It would be highly desirable for C++ to include a type which
  would easily represent very large, fixed size, integral values.  Such a type
  in
  existence is the Binary Coded Decimal (BCD), but any such type would do.
  This type could be implemented as an intrinsic type or a standard library
  class.

  Rationale:  Other languages (e.g., COBOL, yeech!) and also hardware
  architectures (e.g., IBM 370) support the BCD data type, where successive
  decimal digits of a number are stored in successive nibbles, the last nibble
  also indicating sign.  The length, in decimal digits, and position of the
  decimal point of a variable is fixed at declaration.

  With the C language, many businesses have been forced to use awkward
  methods (such as using a long double or multiple longs) to support the
  range of money values required by their applications.  The use of these
  methods is cumbersome.  For example, the use of long double must be
  restricted to whole values (i.e., no fractional values) and scaling the value
  by 100 to print dollars is required.  This is necessary because rounding
  errors in financial applications is simply unacceptable.  Also, using long
  double (or any floating-point type) may have an impact on the performance
  of an application, depending on the hardware support for floating-pointer
  computations. Using multiple longs is even more cumbersome.

  Typical financial applications in large businesses have requirements for up
  to 15 or 16 decimal digits for printed values (13 digits plus 2 or 3 after
  the
  decimal point) and up to 20 or 21 digits for calculations (e.g., bank
  interest
  calculations must maintain principal values at these levels of precision).

  5.5. Thread class

  Comment:  C++ should define a standard Thread or Task class and
  supporting inter-thread co-ordination classes (e.g., Semaphore, Mutex, etc.)
  to support multi-threaded applications. Support for this set of standard
  classes could be made optional, with implementors indicating whether the
  "multi-threaded feature" is supported in their documentation.

  Rationale:  Object-oriented programming (OOP) started out in the field of
  simulation.  OOP also is easily adapted to real-time applications.  Programs
  in both of these application domains utilitize multi-threaded techniques.
  Many other object-oriented languages support multiple threads as a
  language construct (e.g., Ada).  Also, most modern operating systems
  support multi-threaded programming via system level APIs.

  If a standard class (complete with interface, etc.) is not defined by this
  standard, implementors and other third parties will each create there own
  versions, thus making portable multi-threaded programming impossible.  By
  defining an optional, but standard set of classes, this standard would allow
  implementors to choose if they want to include multi-threading features in
  their product and, at the same time, would allow for portable programming
  among the products of implementors that choose to provide the option.

  5.6. renew operator

  Comment:  C++ should support a renew operator which would operate in a
  similar fashion to the realloc function in the C language.

  An example of the suggested usage syntax is as follows.

                 // Allocate 5 elements initially
                 Thing *p = new Thing [5];
                      :
                      :
                 // Need 5 more elements
                 p = renew p [10];    // Note: type of p is known by compiler

  This would require renew to take the form T *operator renew(T *, int),
  where T is the type of each element (Thing in the example, above).

  The semantics would be something like this:  (1) renew would be supported
  only for dynamic arrays (i.e., not single objects); (2) If the number of
  elements, r, in the realloced array is less than the number of elements, a,
  in
  the original array, the last a   r elements of the original array would be
  destroyed (i.e., their destructors would be called, then their memory would
  be freed); (3) If the number of elements, r, in the realloced array is
  greater
  than the number of elements, a, in the original array, r   a new elements
  would be constructed at the end of the array using the default constructor;
  (4) In any case, if the dynamic array must be moved within the free store
  (as determined by the implementation), the copy constructor would be used
  to copy the elements from one location to another and the original elements
  would be destroyed, as in (2), above.  Note that the foregoing discussion is
  acedemic for elements of simple types (e.g., ints, enums, pointers, etc.).

  Rationale:  Many C programmers use the realloc function provided in the C
  standard library for dynamically resizing buffers, etc.  The lack of a
  parallel
  feature in the C++ language seems to be an omission.

  -----

  Please accept my humble apologies if any of these issues has already
  been beaten to death and a decision made long ago.  Thank you very
  much for this opportunity to submit these comments for consideration by the
  committee.

  Allen B. Taylor (allen.taylor@prior.ca or ataylor@spar.ca)

  =========================================================================

  Public Review Comment #6

    Library (Mike Vilot)

  _____________________________________________________________________________
  To: X3SEC
  From: John Mulhern on Thu, Jun 1, 1995 7:12 PM
  Subject: <string> errata
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;1 Jun 1995 18:43:26
  -0500
  Received: from deliverance.empros.com by ems.empros.com (AIX 3.2/UCB
  5.64/4.03)
            id AA58592; Thu, 1 Jun 1995 17:46:20 -0500
  Received: by deliverance.empros.com (AIX 3.2/UCB 5.64/1.0.IBM)
            id AA62455; Thu, 1 Jun 1995 17:45:17 -0500
  From: jmulhern@empros.com (John Mulhern)
  Message-Id: <9506012245.AA62455@deliverance.empros.com>
  Subject: <string> errata
  To: x3sec@itic.nw.dc.us
  Date: Thu, 1 Jun 1995 17:45:17 -0500 (CDT)
  X-Mailer: ELM [version 2.4 PL24]
  Mime-Version: 1.0
  Content-Type: text/plain; charset=US-ASCII
  Content-Transfer-Encoding: 7bit
  Content-Length: 9549



                                                 2 June 1995

  X3 Secretariat
  Attn: Deborah J Donovan
  1250 Eye Street NW, Suite 200
  Washington, DC 20005


  The comments in this document stem from my implementation of the
  <string> header according to the Draft C++ Standard as decribed in Doc
  No X3J16/95-0087 WG21/N0687.  This header was implemented within the
  scope of existing compiler limitations.  In particular, I used the IBM
  xlC compiler and HP reference implementation of STL to implement the
  header <string>.  xlC does not support member function templates,
  template streams, default template parameters, or namespaces, among
  other things.  Nonetheless, with the assistance of STL, I have
  implemented nearly all functionality of the basic_string template class, one
  way or another.  In the process, I have made note of numerous errors in the
  the text of the [lib.strings].  These errors are the subject of this
  document.



  [lib.basic.string]
  Rather than pointing out each syntax error in the declaration of class
  basic_string, I would point out the general error thoughout this
  section.  Except where 'basic_string' is the name of a constructor or
  destructor, 'basic_string' must be modified to:
  'basic_string< charT, traits, Allocator >'.  This error occurs again in
  sections: [lib.string::assign] (the last function), [lib.string::assign]
  (the last function), [lib.string::remove] (the last two functions), and in
  numerous places througout the text of [lib.string::replace] Any decent
  word processor can find all the occurances of this error.

  [lib.string.cons] (and lots of other places...)
  The very last line of the section:  There is no basic_string( charT )
  constructor.  Unless it was the commitee's intent have such a constructor,
  this leads to errors throughout the rest of [lib.strings].  I noticed this
  error in:
      [lib.string.cons] last line at end '( c )' s/b '( 1, c )'
      [lib.string.op+=] last line at end '( c )' s/b '( 1, c )'
      [lib.string::append] append( size_type, charT ) arguments to the
                  return value are given backwards: '( c, n )' s/b '( n, c )'.
      [lib.string::assign] assign( size_type, charT ) arguments to the
                  return value are given backwards: '( c, n )' s/b '( n, c )'.
      [lib.string::insert] insert( size_type, size_type, charT ) arguments to
  the
                  return value are given backwards: '( c, n )' s/b '( n, c )'.
      [lib.string::replace] replace( size_type, size_type, charT ) arguments to

                  the return value are given backwards:'( c, n )' s/b '( n, c
  )'.
      [lib.string::find] last line: '( c )' s/b '( 1, c )'.
      [lib.string::rfind] last line: '( c, n )' s/b '( 1, c )'.
      [lib.string::find.first.of] last line: '( c )' s/b '( 1, c )'.
      [lib.string::find.last.of] last line: '( c )' s/b '( 1, c )'.
      [lib.string::find.first.not.of] last line: '( c )' s/b '( 1, c )'.
      [lib.string::find.last.not.of] last line: '( c )' s/b '( 1, c )'.
      [lib.string::op+] operator+( charT, basic_string<...>& ) in the
                  'Returns:' line, the constructor argument must be '( 1, lhs
  )'
      [lib.string::op+] operator+( basic_string<...>&, charT ) in the
                  'Returns:' line, the constructor argument must be '( 1, rhs
  )'

  [lib.string::replace]
  In the 'Effects:' section for the first replace() function, in the first
  sentence, remove the '&' from in front of the name 'pos1'.

  [lib.string::compare]
  The first compare() function in this section must be declared 'const' as
  it was declared in [lib.basic.string].

  [lib.string.cons]
  For explicit basic_string( Allocator& Allocator() ), Table 38, it seems
  to me that the required value for data() should be '0' because the
  size() == 0, following the requirements given in section
  [lib.string.ops].  The capacity should, however, be left unspecified.
  I can not think of any circumstance in which data() would be other than
  zero for a string of length zero.  On the other hand, I can well imagine
  code expecting a zero-pointer from data() when the string size() is
  zero. c_str() returns a traits::eos() terminated, zero length string for
  a string of size() == 0.  The standard should be more clear that that is
  the case for c_str() since this is what programmers will expect and
  indeed need.

  [lib.basic.string]
     size_type copy( charT*, size_type, size_type )
  This function is not declared to be 'const', but the function is indeed
  'const' with respect to *this.  As the copy() function might be especially
  useful with const strings, I believe the copy() function should be
  declared const.

  [lib.string.op+]
  In the 'Returns' section for the first function, i.e. 'Returns:
  lhs.append( rhs )', surely the committee doesn't intend what is written
  there.  It should be something like 'Returns: basic_string<...>( lhs, rhs );'

  but of course the concatenating constructor is not part of the basic_string
  public interface.  It may well be part of the private interface.

  [lib.string::find]
  Missing a comma between ( s, n ) and pos on the 'Returns' line for
  size_type find( const charT*, size_type, size_type ) const;

  [lib.string::rfind]
  The default value for the 'pos' argument should be '0' and not the
  stated 'npos'.  This applies to the 1st, 3rd and 4th versions of rfind
  as presented.  'pos' refers to the offset into *this from the beginning.
  rfind() searches from its last character to at( pos ) just as find()
  searches from at( pos ) to the last character.
  In the 'Effects' section, the first condition must be identical to
  find()'s first condition, i.e. 'pos <= xpos and xpos + str.size() <= size()'

  [lib.string::find.last.of]
  The default value for the 'pos' argument should be '0' and not the
  stated 'npos'.  This applies to the 1st, 3rd and 4th versions of find_last_of
  as presented.  'pos' refers to the offset into *this from the beginning.
  find_last_of() searches from its last character to at( pos ) just as
  find_first_of() searches from at( pos ) to the last character.
  In the 'Effects' section, the first condition must be identical to
  find_first_of()'s first condition, i.e. 'pos <= xpos and xpos < size()'.

  [lib.string::find.last.not.of]
  The default value for the 'pos' argument should be '0' and not the
  stated 'npos'.  This applies to the 1st, 3rd and 4th versions of
  find_last_not_of() as presented.  'pos' refers to the offset into *this
  from the beginning.  find_last_not_of() searches from its last character to
  at( pos ) just as find_first_not_of() searches from at( pos ) to the last
  character.

  In the 'Effects' section, the first condition must be identical to
  find_first_not_of()'s first condition, i.e. 'pos <= xpos and xpos < size()'.

  [Section 21.1.1.10.8] (which bears no other identifier...)
  operator>>():  It seems to me that, to be useful, operator>>() must eat
  zero or more delimiters specified by basic_string<...>::traits::is_del()
  prior to reading each string.  This should be specifed in the standard,
  to prevent varying implementations.  If that is not the committee's
  intent, it should be explicitly stated in the standard what the intent
  is.

  [lib.string::remove]
  This is the only user experience I have to date concerning my
  implementation of <string> (which I'm still testing).  My compiler, xlC,
  and many others, has trouble with resolving overloading of the calls

  remove( 6 );
  remove( iterator );

  because iterators for basic_string<char> are of
  type char* and char*/int overloading is unresolvable. Now, remove(6)
  calls remove( size_type, size_type ) but xlC throws up on the char*/int
  overload and so never finds remove( size_type, size_type ).  I'm not
  sure what you could do to remedy that situation.  It is nice to say
  remove() to erase the entire string.  Perhaps

  basic_string<....>& remove(); and
  basic_string<....>& remove( size_type, size_type ) with no defaults given.

  Perhaps xlC is misbehaving and this isn't a problem.
  Perhaps this problem exists elsewhere and I haven't encountered it yet
  in user experience.

  [lib.string::insert]
  iterator insert( iterator p, size_type n, charT c = charT() );
  There is no 'Returns' line for this function.  Presumably, this should
  be 'Returns: p'.

  [lib.string.cons]
  Nit picking.  The template constructor:
  template < class InputIterator >
  basic_string( InputIterator begin,
  	      InputIterator end,
  	      Allocator& Allocator() );
  Compilers will probably like this better if the argument names are
  'first' and 'last' rather than 'begin' and 'end'.  This would also be
  consistent with usage everywhere else in the standard with regard to
  iterators.  As a side benefit, the contents of box labelled Table 43
  would then make consistent sense.  The 'Notes:' section, needless to
  say, doesn't make any sense as printed in the draft standard.

  [lib.string::find]
  [lib.string::rfind]
  [lib.string::find.first.of]
  [lib.string::find.last.of]
  [lib.string::find.first.not.of]
  [lib.string::find.last.not.of]
  For all of these functions there should some comment in the standard
  which says that 'pos is the minimum of pos and size()' thereby dealing
  with the otherwise unconstrained argument pos.  These functions do not
  throw exceptions for pos > size().

  [lib.exception]
  Yes, this not part of [lib.strings] but I had to implement <stdexcept>
  in order to implement <string>.  The copy constructor cannot return
  any value, so 'exception& exception( const exception& ) throw();'
  should be 'exception( const exception& ) throw();'.






  John Mulhern
  jmulhern@empros.com

  Siemens Corporation
  Empros Power System Control
  7225 Northland Drive
  Brooklyn Park, MN 55428

  =========================================================================

  Public Review Comment #7

    7.1 General Comments
      This section contains comments that are editorial or of a C
      compatibility nature.
      * Editorial

    7.2 Namespaces
      * Core

    7.3 New-Style Cast Operators
      This section discusses the type conversions that should be
      allowed/disallowed with the new-style casts.
      * Core

    7.4 C Linkage
      * Core

    7.5 The last paragraph of the C Linkage section
      * C compatibility

    7.6 Translation Limits
      * Environment

    7.7 Library - Is it too much?

    7.8 Exception Specifications

    7.9 Granularity

    7.10 Container Concerns

    7.11 Iostream Concerns

    7.12 Clause-by-clause Comments
      Comments on clause 1 to 15: Core.
      Comments on clause 17 to 27: Library.
      Comments on Annex A are for the Syntax WG.
      Comments on Annex B are for the Environment WG.
      Comments on Annex C are for the C Compatibility WG.

  -------------------------------------------------------------------------

  =========================================================================

  Public Review Comment #8

    8.1 - Editorial
    8.2 - Core
    8.3 - Core
    8.4 - Extensions
    8.5 - Extensions
    8.6 - Library
    8.7 - Library

  _____________________________________________________________________________
  To: X3SEC
  From: Stan Friesen on Wed, Jun 7, 1995 7:23 PM
  Subject: Comments on X3J16 Draft
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;7 Jun 1995 19:23:00
  -0500
  Received: from tools3.ElSegundoCA.NCR.COM (tools3.ElSegundoCA.ATTGIS.COM) by
  mailhost.ElSegundoCA.ATTGIS.COM (4.1/SMI-4.1)
  	id AA20607; Wed, 7 Jun 95 16:25:12 PDT
  Date: Wed, 7 Jun 95 16:25:12 PDT
  From: Stan Friesen  <swf@ElSegundoCA.ATTGIS.COM>
  Message-Id: <9506072325.AA20607@mailhost.ElSegundoCA.ATTGIS.COM>
  To: x3sec@itic.nw.dc.us
  Subject: Comments on X3J16 Draft


  8.1
  Section 1.7, paragraph 2 has a typographical error, the phrase
  "diagnosable error" is repeated twice, and there is incorrect
  punctuation.


  8.2
  Section 3.6.2, paragraph 1: I find this paragraph confusing.  To me it
  appears
  as if the statement that objects "initializated with constant expressions are
  initialized before any dynamic ... initialization takes place" is in conflict
  with the statement that within a translation unit, "the order of
  inititialization
  of non-local objects ... is the order in which their definition appears".

  8.3
  Section 3.9, paragraph 9: The term "POD" is used before it is defined. At
  the very least a forward reference to the definition should be placed at the
  first such use.

  8.4
  Section 14.8, paragraph 2: In the example, the last item seems to violate the
  first sentence of the paragraph, in that 'p' doesn't look like a contstant
  expression to me.  I would think that 'p' needs to be declared as
  "char * const" to make it a constant.

  8.5
  Section 16.8, paragraph 1: I think an additional macro that is comparable
  to __STDC__ in that it is unuque to supposedly standard conforming
  implementations.  There are enough new features and changes that programmers
  may want to be able to #ifdef on ANSI conformance.  I cannot see any way to
  do that with the set of macros you define here. [Most existing
  implementations
  already define __cplusplus, so it cannot be used in this way].

  8.6
  Section 20.4.5 (auto_ptr): In the description of operator=(), the line in
  the effects paragraph that says "copies the argument a to *this" is
  effectively
  redundant, since the reset() call mentioned in the next line accomplishes
  exactly that.  I found this confusing when I was implementing this class.

  8.7
  Also, neither release() nor reset() have a "Returns:" paragraph.

  Perhaps the whole library needs to be reviewed with this sort of confusion
  in mind.

  swf@elsegundoca.attgis.com		sarima@netcom.com

  The peace of God be with you.

  =========================================================================

  Public Review Comment #9

    9.1 - Core
    9.2 - Core
    9.3 - Core
    9.4 - Editorial
    9.5 - Extension
    9.6 - Core

  _____________________________________________________________________________
  To: X3SEC
  From: Ronald Fischer on Thu, Jun 8, 1995 8:09 AM
  Subject: comment on C++ draft standard
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;8 Jun 1995 08:07:47
  -0500
  Received: by arl-img-5.compuserve.com (8.6.10/5.950515)
  	id IAA09877; Thu, 8 Jun 1995 08:10:48 -0400
  Date: 08 Jun 95 08:08:53 EDT
  From: Ronald Fischer <100042.2363@compuserve.com>
  To: X3 Secretariat <x3sec@itic.nw.dc.us>
  Subject: comment on C++ draft standard
  Message-ID: <950608120853_100042.2363_EHK54-1@CompuServe.COM>

  Here is my comment on the draft standard for C++. A signed hardcopy will be
  sent
  by mail, as requested.

  ronald.fischer@acm.org

  ---------------------------------------------------------------------

  	COMMENTS ON DRAFT C++ STANDARD

  Minor issues:

  9.1### struct vs. class

  9 par. 4 says:

  "A structure is a class declared with the class-key 'struct'"

  From this, it is not clear if the program fragment

      struct A; // a forward declaration
      class A {public: int i; }

  is valid C++ (according to current practice it is) and, if it is, if A is
  called a structure.

  ----------------------------------------------------------------------

  9.2### Local variables and the scope for function prototype

  3.3.2 says:

  "In a function declaration, names of parameters have function prototype
  scope"

  8.3.6 par. 7 says:

  "Local variables shall not be used in default expressions"

  Consider the following examples:

      // example 1
      int x;
      void f(int x, int y = x);

  Is the default argument for y the global x or the first parameter x?
  I believe that it is the parameter x (according to the first rule).
  But is the whole construct illegal? Is the parameter x a local variable?
  I believe it is, but the draft standard lacks a precise definition of
  the term 'local variable' (at least it is not in the index). Now let's
  change the example slightly:

      // example 2
      int y;
      void f(int x = y, int y = 0);

  Is this valid? We know that the scope of the parameter y ends at the
  end of the function prototype, but where does it begin? When its scope
  covers all of the function prototype, example 2 is essentially
  equivalent to example 1. If it begins at the point of declaration
  (which seems to me more consistent with the spirit of C++, but is not
  stated in the standard), then the declaration is legal and the default
  argument for the parameter x denotes ::y.

  ----------------------------------------------------------------------

  9.3### base-clause of a class

  9 par. 2 says:

  "The name of a class can be used as a class-name even within the
  base-clause of the class-specifier itself"

  To me, this implies that

      class A : A {...};

  would be legal, which is certainly not what was intended.

  **********************************************************************

  Comments and suggestions:

  9.4### Position of cv-qualifiers in dec-specifier-seq

  From the grammar follows clearly, that the cv-qualifier may appear
  either before or after the simple-type-specifier, i.e. that

      const int i = 0;

  and

      int const i = 0;

  are equivalent. All examples use however the first form. I suggest that,
  to emphasize that point, a few examples are written the other style
  (const after int) to clarify the point.

  Reason: According to my personal experience, always positioning the
  cv-qualifier after the type specifier has a didactic advantage when
  teaching C++ to newcomers. I found, that many people have problems
  understanding what data is actually protected by a 'const' qualifier.
  If one does not write the cv-qualifier first in a declaration, a very
  simple rule can be formulated, which considerably helps in understanding
  the meaning of a declaration. The rule is this:

      "A 'const' protects what is immediately to its left"

  For example,
      int**const i; // protects the int**
      int*const* i; // protects the int*
      int const**i; // protects the int

  This rule is of course meaningless, if 'const' is written to the left of
  'int'. I consider the topic important enough that it warrants mentioning
  at least in form of one or two rewritten examples.

  ----------------------------------------------------------------------

  9.5### static array members (9.5.2))

  One anomaly in C++ is the difference between the declaration of static
  array members and arrays which are not members at all. The latter can
  be defined by implicitly define the number of elements:

      int ia[] = {5,3,4}; // has 3 elements

  For static members, this is not possible:

      struct S {
          static ia[3]; // number of members must be stated explicitly
      };
      int S::ia[] = {5,4,3};

  Was the alternative

      struct S {static ia[];]}; // illegal according to draft standard

  never considered?

  ----------------------------------------------------------------------

  9.6### Conversion to void

  12.3.2 par. 1 says:

  "If conversion-type-id is 'void' ..., the program is ill-formed"

  It seems to me an unnecessary restriction to exclude user-defined
  conversions to void, because it is well-defined, when voiding happens.
  Example:

      struct S {operator void(); ...};
      ...
      S x;
      x; // call S::operator void here

  Removal of this rule would make the language more orthogonal.

  One could argue that programs exploiting this feature would possibly
  exhibit surprising behaviour, since the invocation of a voiding
  operator is not always visible, but on the other hand, if redefinition
  of the comma-operator is allowed, redefinition of voiding would do
  no more harm ;-).

  =========================================================================

  Public Review Comment T10

    Environment (Gavin Koch)

_______________________________________________________________________________
To: X3SEC
From: Donald Killen on Thu, Jul 6, 1995 3:26 PM
Subject: C++ spec request

X3 Secretariat
Attn: Lynn Barra
1250 Eye Street - Suite 200
Washington DC 20005
email: x3secitic@nw.dc.us

Ms. Lynn Barra / Ms. Cameron

I've submitted a request that a particular area regarding C++ compilers
be looked at; this is a revision thereof (at the request of Debra Donovan).

As my company, Greenleaf Software, Inc. is heavily into developing
and marketing C++ libraries for all major PC platforms, we are particularly
interested in assuring DLL language independence. We strive to support
products like Visual Basic, Borland's Delphi, etc.. in addition to all major
(and some minor) C++ compilers for DOS, Windows, Win32, and OS/2. One problem
stands out above all others:

C++ compilers must "mangle" same-name (e.g. virtual functions of the same
name in two or more classes) functions in the DLL in order to provide
class-specific names. This, in itself, is not terrible; however the fact
that various supported C++ compiler vendors have chosen to mangle the names
in different ways is worse than terrible--it literally prevents language
independence in class libraries of any size and complexity.

We feel that all compiler vendors should use the same algorithm for mangling.
Hopefully, this would not degrade other features or performance, and the
algorithm should be implemented to function the same regardless of things
like platform, memory model, debug (vs. release), etc.. One possible fly in
the ointment might be conditional compilation; actual source code as seen
by various compilers might be different, causing loss of "sync" in invocation
of a mangling function in the compiler. However, we feel that this part is
something we definitely have control over to some degree.

In any event, the prize of language independence should yield many benefits
for developers. Greenleaf now produces nine C and C++ libraries. Some are
still shipping without language independence, and the ones that are are
either pure C or are C++ class libraries with a "thin C shell" that can be
arranged to provide language independence at some cost to feature list.

Thank you for your attention; I will look forward to some indication of
whether the X3J16 C++ Standards Committee considers this matter closed
(i.e. it's already in the spec), worthy of consideration, too late, or
a stupid idea.

        Donald E. Killen, President
        Greenleaf Software Inc.
        Bent Tree Tower Two - Suite 570
        16479 Dallas Parkway
        Dallas, TX 75248
        (214)248-2561  FAX: (214)248-7830
        email: dkillen@iadfw.net


  =========================================================================

  Public Review Comment T11

    Library (Mike Vilot)

  _____________________________________________________________________________
  To: X3SEC
  From: Stan Friesen on Wed, Jun 21, 1995 4:23 PM
  Subject: Additional cmments on X3J16
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;21 Jun 1995 16:23:45
  -0500
  Received: from tools3.ElSegundoCA.NCR.COM (tools3.ElSegundoCA.ATTGIS.COM) by
  mailhost.ElSegundoCA.ATTGIS.COM (4.1/SMI-4.1)
  	id AA22565; Wed, 21 Jun 95 13:25:55 PDT
  Date: Wed, 21 Jun 95 13:25:55 PDT
  From: Stan Friesen  <swf@ElSegundoCA.ATTGIS.COM>
  Message-Id: <9506212025.AA22565@mailhost.ElSegundoCA.ATTGIS.COM>
  To: x3sec@itic.nw.dc.us
  Subject: Additional cmments on X3J16



  In 20.4.5.1 & 20.4.5.2: I see a possible problem with the specification
  of either the assignment operator or the reset() member function.
  Shouldn't one or the other specify that the object pointed to by the
  previous pointer is deleted?

  As it stands it looks as if an assignment of an auto_ptr<> would
  orphan any object owned by the auto_ptr<> assigned to.

  swf@elsegundoca.attgis.com		sarima@netcom.com

  The peace of God be with you.

  =========================================================================

  Public Review Comment T12

    Extensions (Bjarne)

  _____________________________________________________________________________
  To: X3SEC
  From: Jerry Anderson on Thu, Jun 22, 1995 2:41 PM
  Subject: C++ Suggestions
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;22 Jun 1995 14:41:30
  -0500
  Received: by dub-img-4.compuserve.com (8.6.10/5.950515)
  	id OAA02072; Thu, 22 Jun 1995 14:44:44 -0400
  Date: 22 Jun 95 14:39:56 EDT
  From: Jerry Anderson <73532.1760@compuserve.com>
  To: C++ Commitee <x3sec@itic.nw.dc.us>
  Subject: C++ Suggestions
  Message-ID: <950622183955_73532.1760_EHT104-1@CompuServe.COM>

  To whom it may concern,

  I would recommend the addition of a keyword that served the following
  purpose:

  When a function has been over-ridden in a sub-class and it is necessary to
  call
  the base class implementation of the function also. A keyword denoting the
  base
  class would be useful instead of the explicit reference that is now required.
  For example:

  	void MySubClass::SomeFunction(void)
  	{
  		...
  		MyBaseClass::SomeFunction();
  		...
  	}

  Replace with:

  	void MySubClass::SomeFunction(void)
  	{
  		...
  		base->SomeFunction();
  		...
  	}

  This is handy if the implementation details of the particular sub class are
  ever
  changed, i.e. the base classes and/or functions are reorganized or replaced.

  An example of one of the benefits might be in a case where there are several
  levels of inheritance and the function implementation in question moves from
  one
  class to another (through a redesign requirement). The compiler simply
  resolves
  to the closest implementation (relative to inheritance levels that is).

  This is not intended to remove or replace the explicit call, only to augment
  it.


  Thank you.

  Jerry Anderson
  State of the Art

  =========================================================================

  Public Review Comment T13

    Core (Josee)

    T13.1 - Core
    T13.2 - Core

  _____________________________________________________________________________
  To: X3SEC
  From: jay zipnick on Thu, Jun 22, 1995 5:26 PM
  Subject: C++ Public Review Comment
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;22 Jun 1995 17:11:45
  -0500
  Received: from shell1.best.com (shell1.best.com [204.156.128.10]) by
  blob.best.net (8.6.12/8.6.5) with ESMTP id OAA18502 for
  <x3sec@itic.nw.dc.us>; Thu, 22 Jun 1995 14:13:52 -0700
  Received: (jzipnick@localhost) by shell1.best.com (8.6.12/8.6.5) id OAA29893;
  Thu, 22 Jun 1995 14:13:35 -0700
  Date: Thu, 22 Jun 1995 14:13:34 -0700 (PDT)
  From: jay zipnick <jzipnick@best.com>
  To: x3sec@itic.nw.dc.us
  Subject: C++ Public Review Comment
  Message-ID: <Pine.BSF.3.91.950622135320.25446A-100000@shell1.best.com>
  MIME-Version: 1.0
  Content-Type: TEXT/PLAIN; charset=US-ASCII


  Dear Committee Members:

  Below are two issues I am bringing up for consideration during the Public
  Comment Period of the C++ draft standard (ISO/IEC CD 14882). The first
  deals with arguments which are pointers to incomplete types, and the
  second deals with an apparent hole in the standard regarding function
  pointers and C linkage.

  Any references I make to the draft standard, refers to Doc No. X3J16/95P
  0087, WG21/N068, dated 28 April 1995


  T13.1
  ISSUE 1) Arrays of incomplete types as formal arguments:
  --------------------------------------------------------

  As per 8.3.4, Arrays, paragraph 1, "In a declaration T D where D has the
  form "D1 [ const-expr(opt) ]" ... . T shall not be a reference type, an
  incomplete type, ...".

  ----------
  struct foo;

  void f1(int* arr  );    // legal
  void f2(int  arr[]);    // legal
  void f3(foo* arr  );    // legal
  void f4(foo  arr[]);    // not legal <<--- (subject of this petition)
  ----------

  The bottom line, is that "void f4(foo arr[]);", above, is illegal because
  foo is incomplete. However I would like the committee to consider changing
  this for the reasons outlined below:

  a) Internally, the generated code for "foo* arr", or "foo arr[]" is the
  same, so it has already been implemented by the compiler writer. It is,
  after all, just a pointer (when it gets down to the lowest level). So the
  compiler shouldn't care what it is pointing to in order to pass it as a
  function parameter.

  b) Most C programmers and C++ programmers I have spoken to already
  strongly *believe* this is legal, and have done this kind of code before
  (most compilers I use currently allow this, even though it is not legal).

  This belief, in part, stems from the classic book, "The C Programming
  Language", 2nd ed., Brian W. Kernighan and Dennis M. Ritchie, page 100:

      "Within f, the parameter declaration can read
          f(int arr[]) { ... }
      or
          f(int *arr) { ... }"

  (Of course here, int is a complete type.) The point is that while "void
  f4(foo arr[]);" may technically be illegal, it is certainly legal within
  the *spirit* of the language.

  c) Recently one compiler I use started enforcing this, and IT BROKE MY
  CODE.  Keeping the standard as it is now with respect to this issue will
  almost certainly break more code as compilers do a better job of more
  closely conforming to the language standard. Breaking code is a bad thing.

  d) Using the array syntax is "preferred" when passing arrays. Consider the
  well respected publication "Taligent's Guide to Designing Programs", page
  44:

      "Use [] instead of * for arrays in argument lists, because it is
  clearer."

  e) Consider the following example:

  ----------
  struct SystemMap;	// private implementation detail

  class X
      {
      public:
          // details omitted

      private:
          void func(SystemMap map[]);  // <<--- illegal
      };
  ----------

  In this example, SystemMap is an incomplete type. It is also intentionally
  a private implementation detail, so the type declaration was
  *intentionally* hidden in the .cp file (code like this was written with
  the incorrect understanding, and multiple compilers' acceptance that this
  was legal).  Since this is *not* currently legal, there are two approaches
  to fixing this:

      1) Moving the class declaration from the .cp file to the public .h
         file, and make this hidden implementation detail public. Depending
         on how private this implementation detail is, this option may be
         totally unacceptable. Or,

      2) Changing the declaration of func to use pointer notation, which
         is less clear, and forces one to violate the Taligent, and other
         style guidelines.

  Either solution is undesirable, but as it stands now, unless the standard
  is changed, C++ users will be forced to do one or the other.

  g) The rationale I have heard for keeping this illegal, is to maintain
  consistency with C. (As I understand it, in C this is illegal.) However,
  C++ is a better C. Just because technically this is illegal in C does not
  mean it should be illegal in C++. (There are many things in C++ that are
  not legal in C.) Consistency for the sake of consistency is not enough to
  outweigh the reasons outlined above. Changing this will continue to make
  C++ a better C.


  T13.2
  ISSUE 2) Function pointers and C linkage
  ----------------------------------------

  I have some "portable" code, that may not be portable. What is worse, I
  cannot find any reasonable way within the language to do what I need to do
  without going out-of-my-way to do it.

  Original code:

  ----------
  class foo
      {
      // details omitted
      static int compare(void* key1, void* key2);
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  This compiled under six compilers, then suddenly didn't compile under a
  seventh. The problem is that class foo's implementation uses a C library
  (for handling threaded AVL trees), and this C library needs to be passed
  function pointers.  The seventh compiler has different calling conventions
  for C and C++.  Seeking a *portable* solution, the following change was
  suggested:

  ----------
  class foo    // Modified to work under all seven compilers:
      {
      // details omitted

      #ifdef __SC__
      static int _cdecl compare(void* key1, void* key2);
      #else
      static int        compare(void* key1, void* key2);
      #endif
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  The problem here is that _cdecl is not part of the C++ standard. Passing
  the token _cdecl through other compilers will generate errors. While this
  can be simplified to the following:

  ----------
  #if !__SC__
  #define _cdecl
  #endif
  ...
      static int _cdecl compare(void* key1, void* key2);
  ----------

  this still has a number of problems.

      1) It forces this vendor specific keyword to appear in my source code
         that should be portable, and free of vendor specific extensions.

      2) I will probably need to continue to modify the sources as I expose
         the code to new compilers with the same restrictions, so that _cdecl
         will not be substituted out by the pre-processor for new compilers
         that also use this keyword. Modifying the code as it is exposed to
         new compilers is simply not my definition of portable.

      3) Since _cdecl is not part of the standard, there is nothing to prevent
         another compiler vendor from having their own pragma, or keyword
         (possibly with different placement, altering the syntax), making
         it even more difficult to maintain the code.

  According to the standard, there appears to be implications that the code
  should be portable with no need for _cdecl, or any vendor specific
  language extensions. Specifically:

  a) section 9.5 paragraph 5, has a bracketed example which gives the
  function type of a static member function as a standard C function pointer
  [void(*)()].

  b) section 13.4 paragraph 3 states: "Non-member functions and static
  member functions match targets of type 'pointer-to-function;'"

  There is no mention that I was able to locate in the draft standard which
  distinguished between pointer-to-C-function and pointer-to-C++-function,
  as this compiler did. However, despite this, I have been told by the
  compiler vendor that there is general agreement that C and C++ functions
  can use different calling conventions and there is no guarantee that
  function pointers to the different function types are assignment
  compatible. [Adding extra complexity to the compiler, linker, debugger,
  and currently the C++ programmer -- yuck.]

  If the committee is in agreement that C and C++ functions can use
  different calling conventions, one possible solution to this problem would
  be for the *compiler* to generate a glue function, that would perform the
  translations in calling conventions, and pass the address of such glue
  function. It certainly *seems* like a problem the compiler can solve. But
  with vendor specific keywords, and non-portable code there is currently
  *no* good solution.

  Solutions which require vendor specific extensions should be strongly
  discouraged by the committee. I urge the committee to find a portable
  method of implementing the code above, without major sacrifices by the
  programmer, or compromises to the programming style (e.g. such as writing
  separate wrapper functions with C linkage that call the static member
  functions -- this defeats the whole idea of encapsulating the functions
  within the class).


  Thank you for your consideration of these issues.

  Sincerely,



  Jay Zipnick
  jzipnick@best.com
  Intelligent Resources Integrated Systems, Inc.
  (408) 725-0622


  Sent via US Mail to:
    X3 Secretariat
    Attn.:  Deborah J. Donovan
    1250 Eye St. NW, Suite 200
    Washington, DC  20005

  cc:
    ANSI
    Attn.: BSR Center
    11 West 42nd St.
    New York, NY  10036

  _____________________________________________________________________________
  To: X3SEC
  From: jay zipnick on Wed, Jun 28, 1995 5:11 PM
  Subject: C++ Public Comment (v2)
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;28 Jun 1995 17:02:58
  -0500
  Received: from shell1.best.com (shell1.best.com [204.156.128.10]) by
  blob.best.net (8.6.12/8.6.5) with ESMTP id OAA18536 for
  <x3sec@itic.nw.dc.us>; Wed, 28 Jun 1995 14:04:55 -0700
  Received: (jzipnick@localhost) by shell1.best.com (8.6.12/8.6.5) id NAA22598;
  Wed, 28 Jun 1995 13:47:43 -0700
  Date: Wed, 28 Jun 1995 13:47:33 -0700 (PDT)
  From: jay zipnick <jzipnick@shell1.best.com>
  To: x3sec@itic.nw.dc.us
  Subject: C++ Public Comment (v2)
  Message-ID: <Pine.BSF.3.91.950628134029.21093A-100000@shell1.best.com>
  MIME-Version: 1.0
  Content-Type: TEXT/PLAIN; charset=US-ASCII

  Dear Committee Members:

  [This is to supersede similar comments I submitted on 20-Jun-95. This
  contains the same comments, but slightly reworded for clarity.]

  Below are two issues I am bringing up for consideration during the Public
  Comment Period of the C++ draft standard (ISO/IEC CD 14882). The first
  deals with arguments which are pointers to incomplete types, and the
  second deals with an apparent hole in the standard regarding function
  pointers and C linkage.

  Any references I make to the draft standard, refers to Doc No. X3J16/95P
  0087, WG21/N068, dated 28 April 1995


  T13.1 (Revision 1)
  ISSUE 1) Arrays of incomplete types as formal arguments:
  --------------------------------------------------------

  As per 8.3.4, Arrays, paragraph 1, "In a declaration T D where D has the
  form "D1 [ const-expr(opt) ]" ... . T shall not be a reference type, an
  incomplete type, ...".

  ----------
  struct foo;

  void f1(int* arr  );    // legal
  void f2(int  arr[]);    // legal
  void f3(foo* arr  );    // legal
  void f4(foo  arr[]);    // not legal <<--- (subject of this petition)
  ----------

  The bottom line, is that "void f4(foo arr[]);", above, is illegal because
  foo is incomplete. However I would like the committee to consider changing
  this for the reasons outlined below:

  a) Internally, the generated code for "foo* arr", or "foo arr[]" is the
  same, so it has already been implemented by the compiler writer. It is,
  after all, just a pointer (when it gets down to the lowest level). So the
  compiler shouldn't care what it is pointing to in order to pass it as a
  function parameter.

  b) Most C programmers and C++ programmers I have spoken to already
  strongly *believe* this is legal, and have done this kind of code before
  (most compilers I use currently allow this, even though it is not legal).

  This belief, in part stems from the classic book, "The C Programming
  Language", 2nd ed., Brian W. Kernighan and Dennis M. Ritchie, page 100:

      "Within f, the parameter declaration can read
          f(int arr[]) { ... }
      or
          f(int *arr) { ... }"

  (Of course here, int is a complete type.) The point is that while "void
  f4(foo arr[]);" may technically be illegal, it is certainly legal within
  the *spirit* of the language.

  c) Recently one compiler I use started enforcing this, and IT BROKE MY
  CODE.  Keeping the standard as it is now with respect to this issue will
  almost certainly break more code as compilers do a better job of more
  closely conforming to the language standard. Breaking code is a bad thing.

  d) Using the array syntax is "preferred" when passing arrays. Consider the
  well respected publication "Taligent's Guide to Designing Programs", page
  44:

      "Use [] instead of * for arrays in argument lists, because it is
  clearer."

  e) Consider the following example:

  ----------
  struct SystemMap;	// private implementation detail

  class X
      {
      public:
          // details omitted

      private:
          void func(SystemMap map[]);  // <<--- illegal
      };
  ----------

  In this example, SystemMap is an incomplete type. It is also intentionally
  a private implementation detail, so the type declaration was
  *intentionally* hidden in the .cp file (code like this was written with
  the incorrect understanding, and multiple compilers' acceptance that this
  was legal).  Since this is *not* currently legal, there are two approaches
  to fixing this:

      1) Moving the class declaration from the .cp file to the public .h
         file, and make this hidden implementation detail public. Depending
         on how private this implementation detail is, this option may be
         totally unacceptable. Or,

      2) Changing the declaration of func to use pointer notation, which
         is less clear, and forces one to violate the Taligent, and other
         style guidelines.

  Either solution is undesirable, but as it stands now, unless the standard
  is changed, C++ users will be forced to do one or the other.

  g) The rationale I have heard for keeping this illegal, is to maintain
  consistency with C. (As I understand it, in C this is illegal.) However,
  C++ is a better C. Just because technically this is illegal in C does not
  mean it should be illegal in C++. (There are many things in C++ that are
  not legal in C.) Consistency for the sake of consistency is not enough to
  outweigh the reasons outlined above. Changing this will continue to make
  C++ a better C.


  T13.2 (Revision 1)
  ISSUE 2) Function pointers and C linkage
  ----------------------------------------

  I have some "portable" code, that may not be portable. What is worse, I
  cannot find any reasonable way within the language to do what I need to do
  without going out-of-my-way to do it.

  Original code:

  ----------
  class foo
      {
      // details omitted
      static int compare(void* key1, void* key2);
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  This compiled under six compilers, then suddenly didn't compile under a
  seventh. The problem is that class foo's implementation uses a C library
  (for handling threaded AVL trees), and this C library needs to be passed
  function pointers.  The seventh compiler has different calling conventions
  for C and C++.  Seeking a *portable* solution, the following change was
  suggested by the compiler vendor:

  ----------
  class foo
      {
      // details omitted
      static int _cdecl compare(void* key1, void* key2);
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  The problem here is that _cdecl is not part of the C++ standard. Passing
  the token _cdecl through other compilers will generate errors. This can be
  worked around as follows (making the code ugly and harder to maintain):

  ----------
  class foo    // Modified to work under all seven compilers:
      {
      // details omitted

      #ifdef __SC__
      static int _cdecl compare(void* key1, void* key2);
      #else
      static int        compare(void* key1, void* key2);
      #endif
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  This can be further simplified to the following:

  ----------
  #if !__SC__
  #define _cdecl
  #endif
  ...
   class foo
      {
      // details omitted
      static int _cdecl compare(void* key1, void* key2);
      };
  ...
      tree = tavl_init(foo::compare);    // pass function pointer
  ----------

  This still has a number of problems.

      1) It forces this vendor specific keyword to appear in my source code
         that should be portable, and free of vendor specific extensions.

      2) I will probably need to continue to modify the sources as I expose
         the code to new compilers with the same restrictions, so that _cdecl
         will not be substituted out by the pre-processor for new compilers
         that also use this keyword. Modifying the code as it is exposed to
         new compilers is simply not my definition of portable.

      3) Since _cdecl is not part of the standard, there is nothing to prevent
         another compiler vendor from having their own pragma, or keyword
         (possibly with different placement, altering the syntax), making
         it even more difficult to maintain the code.

  According to the standard, there appears to be implications that the code
  should be portable with no need for _cdecl, or any vendor specific
  language extensions. Specifically:

      a) section 9.5 paragraph 5, has a bracketed example which gives the
         function type of a static member function as a standard C function
         pointer [void(*)()].

      b) section 13.4 paragraph 3 states: "Non-member functions and static
         member functions match targets of type 'pointer-to-function;'"

  There is no mention that I was able to locate in the draft standard which
  distinguished between pointer-to-C-function and pointer-to-C++-function,
  as this compiler did. However, despite this, I have been told by the
  compiler vendor that there is general agreement that C and C++ functions
  can use different calling conventions and there is no guarantee that
  function pointers to the different function types are assignment
  compatible. [Adding extra complexity to the compiler, linker, debugger,
  and currently the C++ programmer -- yuck.]

  If the committee is in agreement that C and C++ functions can use
  different calling conventions, one possible solution to this problem would
  be for the *compiler* to generate a glue function that would perform the
  translations in calling conventions. The compiler would then pass the
  address of this compler generated function. It certainly *seems* like a
  problem the compiler can solve.  But with vendor specific keywords, and
  non-portable code there is currently *no* good solution.

  Solutions which require vendor specific extensions should be strongly
  discouraged by the committee. I urge the committee to find a portable
  method of implementing the code above, without major sacrifices by the
  programmer, or compromises to the programming style (e.g. such as writing
  separate wrapper functions with C linkage that call the static member
  functions -- this defeats the whole idea of encapsulating the functions
  within the class).


  Thank you for your consideration of these issues.

  Sincerely,



  Jay Zipnick
  jzipnick@best.com
  Intelligent Resources Integrated Systems, Inc.
  (408) 725-0622


  Sent via US Mail to:
    X3 Secretariat
    Attn.:  Deborah J. Donovan
    1250 Eye St. NW, Suite 200
    Washington, DC  20005


  cc:
    ANSI
    Attn.: BSR Center
    11 West 42nd St.
    New York, NY  10036

  =========================================================================

  Public Review Comment T14

    Extensions (Bjarne)

    T14.1 - Extensions / C Compatibility
    T14.2 - Extensions

  _____________________________________________________________________________
  To: X3SEC
  From: Steve Meirowsky on Fri, Jun 23, 1995 2:21 AM
  Subject: C++ comments
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;23 Jun 1995 02:20:26
  -0500
  Received: from nwis (root@localhost) by mail.holonet.net with UUCP
  	id XAA12245; Thu, 22 Jun 1995 23:14:26 -0700
  Received: by nwis.com (wcGATE v4)
  	id 38228W Fri, 23 Jun 1995 06:11:06 GMT
  From: steve.meirowsky@nwis.com (Steve Meirowsky)
  Subject: C++ comments
  Date: Fri, 23 Jun 1995 04:27:53 GMT
  Message-Id: <9506230111062499@nwis.com>
  Organization: NWIS BBS in Wichita, KS at 316-262-1829
  To: x3sec@itic.nw.dc.us

  T14.1

  I think C++ should have one or two new numeric types that are integral
  as part of the language.  A 64bit and 128bit longs.  I think the 64bit
  longs should be mandatory!  Also please choose some easy to remember
  name like dlong/qlong or long64/long128.

  Since processor widths are rapidly growing, 32bit integers are
  becoming too small for big numeric work, and a need for standardization
  for wider integers, I think you consider these 64bit and/or 128bit wide
  integers for the future growth of the language.

  I do not have a copy of the C++ draft.  Sorry to bother you if either of
  these are already in the specification.

  Steve Meirowsky
  IFR Systems Inc
  10200 West York Street
  Wichita, KS 67215

  _____________________________________________________________________________
  To: X3SEC
  From: Steve Meirowsky on Fri, Jun 23, 1995 3:54 PM
  Subject: C++ standard
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;23 Jun 1995 15:54:20
  -0500
  Received: from ifrsys (root@localhost) by mail.holonet.net with UUCP
  	id MAA19578; Fri, 23 Jun 1995 12:36:27 -0700
  Received: by ifrsys.com (wcGATE v4)
  	id 38680W Fri, 23 Jun 1995 19:27:10 GMT
  From: steve.meirowsky@ifrsys.com (Steve Meirowsky)
  Subject: C++ standard
  Date: Fri, 23 Jun 1995 19:06:55 GMT
  Message-Id: <9506231427106474@ifrsys.com>
  Organization: IFR BBS in Wichita, KS at 316-524-0270
  To: x3sec@itic.nw.dc.us

  This is following up from a message that I sent you last night from my
  other email account...


  (more on) T14.1

  After talking to fellow C/C++ programmers at work...the number one item
  that makes us mad about the language is there is not a portable way to
  specify the size (range) of integers (char/int/short/long).  They are
  different on every platform under the sun.

  We would like the following to be an integral part of the language.  I'm
  sure your response will be....use typedef, #define, or a class...but
  that is not good enough for us.  We are tired of not have a specific
  size integer as part of the language.

  int8/int16/int32/int64/int128 are simple to understand the size/range
  of the storage and number range.  It doesn't have to be 'int' since
  'bin' or 'byte' would suffice.

  ---
  T14.2

  The second wish list item is ranges on 'case' statements similar to
  Pascal.  For example, 'case 9..49:'.  We really don't care about the
  method...just that we have it in the language.

  ---

  I did hear about the new keywords for comparison operators such as
  AND/OR being added.  Great!  Some one in the standard group should
  be focused on improving and tweaking the underlying C language.
  Too much is being focused on the new C++ object features!

  ---

  =========================================================================

  Public Review Comment T15

    Extensions (Bjarne)

    T15.1 - Extensions
    T15.2 - Extensions

  _____________________________________________________________________________
  To: X3SEC
  From: Noel Yap on Mon, Jun 26, 1995 3:48 PM
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;26 Jun 1995 15:44:36
  -0500
  Received: from  by psi.com (8.6.10/2.1-PSI/PSINet)
  	id PAA25018; Mon, 26 Jun 1995 15:48:02 -0400
  Received: by  (5.0/SMI-SVR4)
  	id AA16547; Mon, 26 Jun 1995 15:47:36 -0400
  Date: Mon, 26 Jun 1995 15:47:36 -0400
  From: nyap@garban.com (Noel Yap)
  Message-Id: <9506261947.AA16547@>
  To: x3sec@itic.nw.dc.us
  X-Sun-Charset: US-ASCII
  content-length: 1332

  Dear Ms Donovan:
  	I would like to submit for review to X3J16/WG21 two comments.

  T15.1
  	1.	friend granularity
  	One often wishes to grant a class, C0, or a function, f0, permission to
  change the value of a member, m1, of another class, C1.  Usually, either a
  public set function is written (which grants global change permission), or C1
  declares C0 or f0 as a friend (which grants to C0 or f0 complete access to
  C1).  Since neither of these two choices is near optimal, I propose that
  member functions should be able to declare their friends:

  		void
  		C1::set_m1(int i)
  		{
  			friend C0;
  			friend f0(void);

  			m1 = i;
  		}


  T15.2
  	2.	enum conversion overriding
  	If conversion functions from one type, C0, to an enum type, E1, were
  allowed, bool could then be implemented as an enum:

  		enum bool
  		{
  			false,
  			true
  		};

  		bool::bool(int i)
  		//	or, bool bool(int i)
  		//	or, operator bool(int i)
  		{
  			return ((!i)   ?   false   :   true;
  		}


  	This original hard copy will be sent to
  		X3 Secretariat
  		Attn: Deborah J Donovan
  		1250 Eye St NW, Suite 200
  		Washington, DC  20005

  	and a copy sent to
  		ANSI
  		Attn: BSR Center
  		11 W 42nd St
  		New York, NY  10036

  	unless I am notified to do otherwise (my email is nyap@garban.com).
  	Thank you for your time.  I hope these suggestions will help to improve the
  language.

  					Noel Yap

  =========================================================================

  Public Review Comment T16

    Core (Josee)

    T16 - Core
    T16.1
    T16.2
    T16.3
    T16.4

  __________________________________________________________________________
  To: X3SEC
  Cc: Deborah Donovan; David Sachs; ark@research.att.com
  From: sachs@fnal.fnal.gov on Thu, Jun 29, 1995 12:08 PM
  Subject: C++ proposed standard
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;29 Jun 1995 12:08:27
  -0500
  Received: from fnclub.fnal.gov by inet-gw-1.pa.dec.com (5.65/24Feb95)
  	id AA27680; Thu, 29 Jun 95 09:02:01 -0700
  Received: by fnclub.fnal.gov (AIX 3.2/UCB 5.64/4.03)
            id AA40183; Thu, 29 Jun 1995 11:02:00 -0500
  Message-Id: <9506291602.AA40183@fnclub.fnal.gov>
  To: x3sec@itic.nw.dc.us
  Cc: David Sachs <sachs@fnal.fnal.gov>, ark@research.att.com,
          ddonovan@itic.nw.dc.us
  Subject: C++ proposed standard
  Reply-To: sachs@fnal.fnal.gov
  Date: Thu, 29 Jun 95 11:02:00 -0600
  From: David Sachs (Fermilab mail:sachs@fnal.fnal.gov)
  <b91926@fnclub.fnal.gov>



  Comments about proposed ANSI/ISO C++ standard

  David Sachs
  1069 Rainwood Drive
  Aurora, IL 60506-1351

  sachs@fnal.fnal.gov

  T16.1
  I) [class.mi] Section 10.1

  All the examples in this section show only the case where all
  copies of a duplicated base class are indirect. The only discussion
  of the structurally simpler but lexically more complex case, in
  which there is a direct copy and 1 or more indirect copies, that
  I could find was in section 12.6.2 [class.base.init], and the
  language there clearly affirmed the legality of a class so
  designed.

  In view of the clear legality of a class with distinct direct and
  indirect copies of the same base class, the C++ standard needs to
  specify proper syntax for:

    a) referring to members of the distinct bases
    b) casting a pointer (or reference) to an object of a derived
       class to a pointer (or reference) of each one of the distinct
       base class subobjects.

  T16.2
  II) [class.base.init] Section 12.6.2

  There is no discussion of the case of a mem-initializer that specifies
  a name denoting both a nonstatic data member and a direct or virtual
  base class. Declaring such an initialize to be ill formed would be a
  reasonable resolution.

  T16.3
  III) [class.base.init] Section 12.6.2

  When are parameters of mem-initializers evaluated?

  Language in this section clearly hints that the intent of the
  standards committee is that each mem-initializer should be treated as
  a complete expression with its parameters evaluated after all
  previous initialization. However, such a requirement is NOT stated
  explicitly.

  This leaves in limbo code like

  	class x{
  	  int a;
  	  int b;
  	  x(int i) : a(i), b(a) {...}
  	  ...};

  which assumes the presence of a sequence point that guarantees
  that the initializer for b will use the value of a AFTER a is
  initialized.  I have seen published code that blithely assumes such
  sequence points.

  I would have preferred the alternate resolution, that there are no
  such gratuitous sequence points, and that the state of an object is
  undefined while mem-initializer parameters are evaluated.

  T16.4
  IV) [class.copy] section 12.8

  The requirement that a constructor for a class X of the form
  X(volatile X&) or X(const volatile X&) is NOT a copy constructor,
  and the similar requirement for operator= should be EMPHASIZED,
  rather than relegated to an appendix.

  I would suggest including an example like the following:

  class X
  {
    public: X(volatile X&) {} // NOT a copy constructor
   public:
  };

  X a;
  X b=a;  // error - ambiguous - X(volatile X&) or default X(const X&)


  ---
  ** The Klingons' favorite food was named by the first earthling to see it **
  David Sachs - Fermilab, HPPC MS369 - P. O. Box 500 - Batavia, IL 60510
  Voice: 1 708 840 3942      Department Fax: 1 708 840 3785

  =========================================================================

  Public Review Comment T17

    Extensions / Library (Bjarne/Mike)

  _____________________________________________________________________________
  To: X3SEC
  From: M. K. Shen on Fri, Jun 30, 1995 8:27 AM
  Subject: Comment on ISO/IEC CD 14882
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;30 Jun 1995 08:26:36
  -0500
  Received: by cd1.lrz-muenchen.de; Fri, 30 Jun 95 14:13:18 +0200
  Date: Fri, 30 Jun 1995 14:13:18 +0200 (MET)
  From: "M. K. Shen" <Mok-Kong.Shen@lrz-muenchen.de>
  To: x3sec@itic.nw.dc.us
  Subject: Comment on ISO/IEC CD 14882
  Message-ID: <Pine.BSD.3.91.950630140312.28967A-100000@cd1.lrz-muenchen.de>
  MIME-Version: 1.0
  Content-Type: TEXT/PLAIN; charset=US-ASCII

  Dear Sirs,

  Hierwith I am submitting to you a comment on ISO/IEC CD 14882.
  A signed original hardcopy will be sent to you today via airmail.
  The hardcopy might not arrive prior to July 6 but certainly before
  July 25.

  Sincerely yours,
  Mok-Kong Shen


  -------


  Comment on ISO/IEC CD 14882
  ---------------------------

  Subject:  Multidimensional Arrays (8.3.4)

  Abstract:  The C++ multidimensional arrays are inferior to those of e.g.
  Fortran and thus need to be improved for the language to gain wider
  acceptance in the fields of engineering and scientific numerical
  computations hithertofore absolutely dominated by Fortran.  It is
  suggested that a new data type be added to the C++ standard for that
  purpose.

  The multidimensional arrays of C++ are the same as those of C.  The
  defect of this data type lies in the inconvenience/inefficiency when a
  multidimensional array is passed to a subprogram which is written to
  handle an array of not fixed but arbitrary size.  Thus a Fortran
  subprogram of the type

        subroutine sub(m, n1, n2)
        real m(n1,n2)
        m(1,2) = 3
        . . . . . . . . . .  . .
        end

  cannot be simply transcribed into C/C++.  This problem is well-known.
  Stroustrup [1] shows that a subprogram to print an arbitrary integer
  matrix has to be written in an obscure (word his) way using expression
  ((int *) m)[i*dim2 + j] instead of m[i][j].  Further, his example
  subprogram has to be called with print_mij((int **) m, n1, n2) instead
  of the more natural form print_ij(m, n1, n2) expected by the user.  The
  negative software engineering consequence of this needs no arguing.  In
  my personal opinion this 'artificial' complexity is one of the major
  psychological factors hindering most of Fortran programmers from being
  friends of C/C++.

  In practice one overcomes this problem by using pointer arrays.  Thus
  Press et al. [2] employ special subprograms that allocate pointer
  arrays referencing the individual rows of matrices such that in the
  other C subprograms provided by these authors the familiar notation
  m[i][j] can be used.  In C++ one can write a matrix class with operator
  definition to allow simple subscripting of matrix elements while at
  the same time making the essential matrix operations available.  This
  is fine.  However, in situations where the class library is not
  available or cannot be used for portability or other reasons and the
  programmer has to write all himself, the problem remains.  Moreover,
  using such a matrix class is essentailly employing pointer arrays
  behind the scene and thus can incur loss of machine efficiency.
  Disregarding the efficiency issue arising from calling the operator
  function [] of such a class, the efficiency loss can be measured by
  comparing the computing time of using m[i][j] versus pt[i][j] where
  pt[i] points to m[i][0].  Multiplying two matrices of size 300*300
  I obtained a cpu time ratio of 1 : 1.11 on an IBM RISC 6000.  This
  overhead of more than 10% must certainly be regarded as very
  significant by those who constantly strive to optimize their code in
  essential numerical applicatons.

  Therefore I propose that in addition to the currently existing
  multidimensional array data type of C/C++ there be introduced into the
  C++ standard a new array data type that is akin in functionality to
  that of the other major programming languages used in numerical
  computations.  By adding a keyword 'array' one could define e.g.

        double array m[300][300];

  for passing to a subprogram of the type

        void sub(double array [][], int n1, int n2)

  to be referenced inside the subprogram with the m[i][j] notation.
  Preferrably there should also be inquiry functions to determine the
  extent of each dimension of a multidimensional array so that the values
  n1 and n2 above need not be passed through the parameter list.

  It may be noted that the proposed addition does not affect/break
  existing C/C++ code, hence no question of compatibility can arise.
  There is further no problem of implementation as the proposed data type
  is long present in a number of major programming languages.  Therefore
  the adoptation of this proposal should be easy.

  Literature:

  [1]  Bjarne Stroustrup, The C++ Programming Langauge.  2nd ed.,
       p. 128-129. Addison-Wesley, 1991.

  [2]  William H. Press et al., Numerical Recipes in C.  2nd ed.,
       p. 20-23. Cambridge University Press, 1992.

  Submitter of comment:

  Mok-Kong Shen
  (signed) June 30, 1995

  Postal address:                  E-mail:
  Postfach 340238                  shen@lrz-muenchen.de
  D-80099 Muenchen                 (invalid after August 30, 1995)
  Germany

  =========================================================================

  Public Review Comment T18

    Templates / Extensions (Bjarne)

  _____________________________________________________________________________
  To: X3SEC
  From: Boris Rasin on Tue, Jul 4, 1995 4:24 PM
  Subject: C++ Public Review Comment
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;4 Jul 1995 16:23:55
  -0500
  Received: from BORISRAS.netvision.net.il (ts31p2.NetVision.net.il
  [194.90.3.102]) by dns.netvision.net.il (8.6.12/8.6.9) with SMTP id XAA02358
  for <x3sec@itic.nw.dc.us>; Tue, 4 Jul 1995 23:27:45 +0300
  Date: Tue, 4 Jul 1995 23:27:45 +0300
  Message-Id: <199507042027.XAA02358@dns.netvision.net.il>
  X-Sender: brasin@netvision.net.il
  X-Mailer: Windows Eudora Version 1.4.4
  Mime-Version: 1.0
  Content-Type: text/plain; charset="us-ascii"
  To: x3sec@itic.nw.dc.us
  From: brasin@netvision.net.il (Boris Rasin)
  Subject: C++ Public Review Comment

  Subject: Template argument deduction [temp.deduct].
  Proposed addition: Class template argument deduction.

  In a call to class template constructor, class template arguments can be
  deduced from constructor arguments, under the rules for function template
  argument deduction.

  Example:
          class Mutex { ... };
          class Semaphore { ... };
          template <class T> class Lock { ... };

          Mutex M;
          Semaphore S;
          Lock L1 (M); // Lock<Mutex> L1 (M);
          Lock L2 (S); // Lock<Semaphore> L2 (S);

  Boris Rasin (brasin@netvision.net.il)
  July 4, 1995

  =========================================================================

  Public Review Comment T19

    T19.1 to T19.3 - Core
    T19.4 to T19.3 - Library

  _____________________________________________________________________________
  To: X3SEC
  Cc: olsen@Rational.COM
  From: David Olsen on Sat, Jul 1, 1995 8:04 PM
  Subject: C++ Public Comments
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;1 Jul 1995 20:03:46
  -0500
  Received: from igor.Rational.COM (igor.rational.com [89.64.2.102]) by
  rational.com (8.6.9/8.6.9) with SMTP id RAA28263 for <x3sec@itic.nw.dc.us>;
  Sat, 1 Jul 1995 17:08:42 -0700
  Received: from rational.com by igor.Rational.COM (4.1/smh-1.0)
  	id AA05081; Sat, 1 Jul 95 17:07:05 PDT
  Received: from picard.vwo.verdix.com (picard.rational.com [192.41.137.139])
  by rational.com (8.6.9/8.6.9) with SMTP id RAA28258 for
  <@igor.rational.com:x3sec@itic.nw.dc.us>; Sat, 1 Jul 1995 17:08:38 -0700
  Received: by picard.vwo.verdix.com via SMTP (920330.SGI/kato_sgi.950529)
  	for @igor.rational.com:x3sec@itic.nw.dc.us id AA25338; Sat, 1 Jul 95
  17:02:25 -0700
  Message-Id: <9507020002.AA25338@picard.vwo.verdix.com>
  X-Mailer: exmh version 1.5.3 12/28/94
  To: x3sec@itic.nw.dc.us
  Cc: olsen@Rational.COM
  Subject: C++ Public Comments
  Mime-Version: 1.0
  Content-Type: text/plain; charset="us-ascii"
  Date: Sat, 01 Jul 1995 17:02:24 -0700
  From: David Olsen <olsen@Rational.COM>

  Enclosed are some public comments for the draft C++ standard.  I will
  be sending a hardcopy version of these, though probably not in time for
  the meeting on July 9.

  	David Olsen
  	1600 NW Compton Dr. #357
  	Aloha, OR 97006-1992
  	olsen@rational.com

  ========================================================================
  T19.1

  Section 2.8 [lex.key], paragraph 4 lists new[], delete[], new<%%>, and
  delete<%%> as tokens.  new<%%> and delete<%%> are not mentioned
  anywhere else in the document that I can find.  They should be listed
  in Section 2.4 [lex.digraph] as alternate representations for new[]
  and delete[] respectively.

  ========================================================================
  T19.2

  Section 5.3.5 [expr.delete], paragraph 1 contains the following syntax
  for a delete-expression.

  	delete-expression:
  		::opt delete cast-expression
  		::opt delete [ ] cast-expression

  One more possibility should be added.

  		::opt delete[] cast-expression

  If a program does not contain any whitespace between the word delete
  and the pair of brackets, then the compiler must interpret it as a
  single delete[] token, not as three separate tokens (delete, [, and
  ]).  But the delete[] token is not part of a valid delete-expression,
  resulting in a syntax error.

  ========================================================================
  T19.3

  I have some concerns about the example in Section 9.8 [class.nest],
  paragraph 1.  The relevent parts are quoted here:

  	int x;
  	class enclose {
  	public:
  	    int x;
  	    class inner {
  		void g(enclose* p, int i)
  		{
  		    p->x = i; // ok: assign to enclose::x
  		}
  	    };
  	};

  I would like to argue that the line "p->x = i;" is an error because
  the class enclose is incomplete, but I can find no clear statement of
  exactly when a class becomes complete.  The closest I can find is in
  Section 9, paragraph 2: "A class is considered defined after the
  closing brace of its class-specifier has been seen even though its
  member functions are in general not yet defined." and Section 3.9,
  paragraph 6: "Arrays of unknown size and classes that have been
  declared but not defined are called incomplete types."

  According to these sentences, most member functions defined within the
  class definition would be an error because their containing class is
  still not complete.  The passage in the ARM that says that member
  functions defined within the class definition act as if they were
  defined just after the class definition has been removed from the
  draft standard.  So, while section 9.3 states that a member function
  has access to all names declared in its containing class, it does not
  state that the containing class is considered a complete type.  It is
  obviously the committee's intent that inline member function
  definitions be legal, so it is my opinion that the wording of the
  standard be changed to make that clear.

  While it is clear that a class should be considered complete within
  its own member functions, it is not clear that it should be considered
  complete within a nested class and within a nested class's member
  functions.  If a class were considered complete within a nested class,
  it would be possible to consttruct a class that contained itself.

  	struct outer {
  	    int a;
  	    struct inner {
  		int b;
  		outer c;
  	    };
  	    inner d;
  	};

  No such circularities can arise if a class is considered complete
  within a nested class's member functions (but not within the class
  itself), as the original quoted example assumes.  But I would argue
  that this should not be the case and that the example should be an
  error.  If it were legal, then a compiler translating the above
  example would have to delay processing of the member function g()
  until it had finished processing the class enclose.  Member function
  processing can't be delayed just to the end of the class, it would
  have to be delayed until the end of all enclosing classes.  This would
  impose a burden on implementers that would have little benefit for
  programmers.

  Whichever way the committee does decide, the wording of the standard
  should be made more clear.  My suggestion would be to change the end
  of Section 9, paragraph 2 to read: "A class is considered defined
  after the closing brace of its class-specifier has been seen, as well
  as within the function bodies, default arguments, and constructor
  initializers in the class itself (but not necessarily within such
  things in nested classes)."  If this wording is adopted the example in
  Section 9.8, paragraph 1 would also have to be changed.

  ========================================================================
  T19.4

  In Section 20.4.5.2 [lib.auto.ptr.members], it is never specified what
  the member functions auto_ptr<X>::release and auto_ptr<X>::reset
  should return.

  ========================================================================
  T19.5

  Section 24.3.1.1 [lib.reverse.bidir.iter] contains the description of
  the template class reverse_bidirectional_iterator.  The member
  functions base() and operator*() do not change the object on which
  they are called, and should therefore be constant member functions.
  This would affect both the class definition in 24.3.1.1 and the
  descriptions of the two members in 24.3.1.2.2 and 24.3.1.2.3.

  The same argument applies to the template class reverse_iterator and
  its member functions base() and operator*() in Sections 24.3.1.3,
  24.3.1.4.2, and 24.3.1.4.3.

  ========================================================================
  T19.6

  In Section 24.3.1.2.5 [lib.reverse.bidir.iter.op--], the return value
  of reverse_bidirectional_iterator<B,T,R,D>::operator--() is not
  specefied.  There is a Returns clause, but it is empty.

  ========================================================================
  T19.7

  Section 24.3.1.2.6 [lib.reverse.bidir.iter.op==] contains the
  description for reverse_bidirectional_iterator<B,T,R,D>::operator==.
  The Returns clause states:

  	Returns: BidirectionalIterator(x) == BidirectionalIterator(y)

  This assumes that there exists a conversion from a
  reverse_bidirectional_iterator to the BidirectionalIterator class on
  which it is based.  This was true in early versions of STL, but is not
  the case in the current draft standard.  The conversion operator has
  been replaced by the member function base().  Therefore, the Returns
  clause should be changed to either:

  	Returns: x.current == y.current

  or:
  	Returns: x.base() == y.base()

  both of which are equivalent.

  ========================================================================
  T19.8

  Section 24.3.1.3 [lib.reverse.iterator] contains the description of
  the template class reverse_iterator.  At the end of the class
  definition are declarations of operator==, operator<, operator-, and
  operator+.  These should not be in the class definition, but should be
  non-member functions.

  ========================================================================
  T19.9

  Section 24.3.1.4 [lib.reverse.iter.ops] does not contain any
  description for many of the reverse_iterator operators: the default
  constructor for reverse_iterator; the member functions operator+,
  operator+=, operator-, and operator-=; and the non-member functions
  operator<, operator-, and operator+.

  ========================================================================
  T19.10

  Section 25.1.3 [lib.alg.find.end] describes the template function
  find_end.  The complexity clause states:

  	Complexity: At most last1 - first1 applications of the
  corresponding predicate.

  find_end is almost exactly like the template function search (25.1.9)
  except that it finds the last occurance rather than the first.  The
  complexity of search is quadratic ((last1 - first1) * (last2 -
  first2)) rather than linear.  Footnote 196 in Section 25.1.9 explains
  that, while a linear algorithm exists, it is slower in most practical
  cases.  I don't see why the reason for making search quadratic should
  not apply to find_end as well.  In my opinion, the complexity clause
  for find_end should be changed to:

  	Complexity: At most (last1 - first1) * (last2 - first2)
  applications of the corresponding predicate.

  ========================================================================
  T19.11

  Section 25.1.4 [lib.alg.find.first.of] describes the template function
  find_first_of.  I see problems with both the Returns and Complexity
  clauses.

  The Returns clause states:

  	Returns: The first iterator i in the range [first1,
  last1-(last2-first2)) such that for any non-negative integer n <
  (last2-first2), the following corresponding conditions hold: *i ==
  *(first2+n), pred(i, first2+n) == true.  Returns last1 if no such
  iterator is found.

  As I read this, every member of the range [first2, last2) must be
  equal, since the result must compare equal to every one of them.  My
  guess is that it was intended for the result to compare equal to any
  one member of the range [first2, last2), in which case the Returns
  clause should read:

  	Returns: The first iterator i in the range [first1, last1)
  such that there exists some non-negative integer n < (last2-first2)
  where the following corresponding conditions hold: *i == *(first2+n),
  pred(i, first2+n) == true.  Returns last1 if no such iterator is
  found.

  The Complexity clause for find_first_of states:

  	Complexity: Exactly find_first_of(first1, last1, first2+n)
  applications of the corresponding predicate.

  But find_first_of(first1, last1, first2+n) is not a legal function
  call, and find_first_of returns an iterator, not a number.  So the
  Complexity clause just doesn't make any sense.  And given that the
  Returns clause didn't make sense either, I am not sure what the
  complexit should be.

  ========================================================================
  T19.12

  Section 25.1.9 [lib.alg.search] describes the template function
  search.  There are four overloaded version of the function:

  template<class ForwardIterator1, class ForwardIterator2>
  ForwardIterator1
  	search(ForwardIterator1 first1, ForwardIterator1 last1,
  	       ForwardIterator2 first2, ForwardIterator2 last2);

  template<class ForwardIterator1, class ForwordIterator2,
  	 class BinaryPredicate>
  ForwardIterator1
  	search(ForwardIterator1 first1, ForwardIterator1 last1,
  	       ForwardIterator2 first2, ForwardIterator2 last2,
  	       BinaryPredicate pred);

  template<class ForwardIterator, class Size, class T>
  ForwardIterator
  	search(ForwardIterator first, ForwardIterator last,
  	       Size count, const T& value);

  template<class ForwardIterator, class Size, class T,
  	 class BinaryPredicate>
  ForwardIterator
  	search(ForwardIterator first, ForwardIterator last,
  	       Size count, T value, BinaryPredicate pred);

  But there is an overload ambiguity between the first and third
  versions and between the second and fourth versions.  For example,
  given the following code:

  	int *f1, *l1, *f2, *l2;
  	// Set f1, l1, f2, and l2 to be valid iterators
  	search(f1, l1, f2, l2);

  The call to search could match the first version with both
  ForwardIterator1 and ForwardIterator2 as (int *), or it could match
  the third version with ForwardIterator, Size, and T all as (int *).  I
  cannot think of any case where the first or second versions would be
  better matches than the third or fourth versions.  Therefore, I think
  the third and fourth versions of search should be renamed to something
  different.

  ========================================================================
  T19.13

  Section 26.2.1 [lib.complex] contains the definition of the template
  class complex.  The definition contains three different constructors:

  	complex();
  	complex(T re);
  	complex(T re, T im);

  Section 26.2.3 [lib.complex.members], however, only contains a
  description of a single constructor with default arguments:

  	complex(T re = T(), T im = T());

  Either of these sections should be changed to match the other one.

  =========================================================================

  Public Review Comment T20

    Extensions (Bjarne)

  _____________________________________________________________________________
  To: X3SEC; ark@research.att.com
  Cc: donorgan@ix.netcom.com
  From: Don Organ on Wed, Jul 5, 1995 2:17 AM
  Subject: Public Comment for C++: static virtual
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;5 Jul 1995 02:16:55
  -0500
  Received: from megatest.UUCP by uu2.psi.com (5.65b/4.0.940727-PSI/PSINet) via
  UUCP;
          id AA10122 for ; Wed, 5 Jul 95 02:13:07 -0400
  Received: from plethorax.megaeng by megatest (4.1/MEGT-1.1)
  	id AA02387; Tue, 4 Jul 95 23:04:06 PDT
  Received: from dawn.megaeng by plethorax.megaeng (4.1/SMI-4.1)
  	id AA28007; Tue, 4 Jul 95 23:02:11 PDT
  Received: by dawn.megaeng (5.x/SMI-SVR4)
  	id AA05112; Tue, 4 Jul 1995 23:04:50 -0700
  Date: Tue, 4 Jul 1995 23:04:50 -0700
  From: dorgan@Corp.Megatest.COM (Don Organ)
  Message-Id: <9507050604.AA05112@dawn.megaeng>
  To: ark@research.att.com, x3sec@itic.nw.dc.us
  Subject: Public Comment for C++: static virtual
  Cc: donorgan@ix.netcom.com


   from: Don Organ, Megatest Corp. (408) 441-3123: dorgan@megatest.com
   to: X3 Secretariat (e-mail & paper), ANSI (paper), Andrew
  	Koenig (e-mail & paper).
   subject: Public comment
   re: Doc: X3J16/95-0087 WG21/N0687: "Programming Language C++"
   date: July 4, 1995


  As a formal "public comment" there is one aspect of the language I'd like
  to be reconsidered: static virtual member functions.

  I feel that the draft's prohibition against static virtual member functions
  is unnecessary and counter-productive, and suggest that the prohibition
  should be removed.
  Below is:
  	formal description of the suggested changes to the draft
  	a narrative rationale
  	my response to D&E's "Criteria" (although I don't consider this
  		an "extension")
  	interest extracted from comp.std.c++

  Although this static virtual issue is minor compared to the many other
  issues you must address, I hope you will give it the serious consideration
  it deserves. If I can be of any assistence, please don't hesitate to
  contact me.

  I appreciate the efforts of all who have worked toward the development
  and standardization of the C++ language. I wish you continued success
  in this standardization effort. I only wish I could have a working draft
  C++ compiler today!

  Thanks.



  ===================================================
  Don Organ                       dorgan@megatest.com
  Direct/VM: (408) 441-3123       FAX: (408) 451-3201
  Megatest Corp. 880 Fox Lane San Jose, CA 95131-1685
  ===================================================



  Formal Description of the suggested changes to the April draft
  ------------------------------------------------------------------------------
  Intention: remove the prohibition of "static virtual" member functions.

  7.1.2 [dcl.fct.spec]: change 4th paragraph to:
  "The virtual specifier shall be used only in declarations of class member
  functions within a class declaration; se 10.3"
  (remove the word 'nonstatic': was "... in declarations of nonstatic member
  functions ...")

  9.4.1 [class.mfct.nonstatic]: Note that this section is about nonstatic
  member functions. Since the 2nd paragraph already also covers static,
  I'm adding virtual static as well. Perhaps these both should be moved
  to 9.5. I haven't drafted the language for this move, but would be happy
  to do so. Anyways, here's the changes to the 1st paragraph:
  "A nonstatic member function *(begin new language)*, or a static
  virtual member function *(end new language)* may be called for an object
  of its class type, or for an object of a class derived (10) from its class
  type, using the class member access syntax (5.2.4, 13.3.1.1). A
  *(remove 'nonstatic')* member function may also be called directly
  using the function call syntax(5.2.2, 13.3.1.1)
  - from within the body of a member function of its class or of a class
  derived from its class, or
  - from a mem-initializer (12.6.2) for a constructor for its class or for
  a class derived from its class."
  (changes indicated inline)



  9.4.1 [class.mfct.nonstatic]: 2nd paragraph change to:
  "When an id-expression (5.1) that is not part of a class member access
  syntax (5.2.4) and not used to form a pointer to member (5.3.1) is
  used in the body of a nonstatic member function of class X or used in
  the mem-initializer for a constructor of class X, if name lookup (3.4)
  resolves the name in the id-expression to a nonstatic nontype member
  of class X or of a base class of X, *(begin new language)* or a
  virtual static member of class X or of a base class of X, *(end new
  language)*
  the id-expression is transformed into a class member access expression
  (5.2.4) using (*this) (9.4.2) as the postfix-expression to the
  left of the . operator.  The member name then refers to the member of thee
  object for which the function is called. Similarly during name lookup,
  when an unqualified-id (5.1) used in the definition of a member function
  for class X resolves to a static *(begin new language)* non-virtual
  *(end new language)* member, an enumerator or a nested type of X or of
  a base class of X, the unqualified-id is transformed into a qualified-id
  (5.1) in which the nested-name-specifier names the class
  of the member function. [Example: ..."

  9.4.1 [class.mfct.nonstatic]: paragraph 5 (again, since this section
  is about nonstatic member functions, I'm not sure this change is
  appropriate): change to "A member function may be declared virtual (10.3)
  or pure virtual (10.4)."
  (Removed the word 'nonstatic': was "A nonstatic member function ...")


  9.5.1 [class.static.mfct]: Change the 2nd paragraph to read:
  "[Note: a static member function does not have a this pointer (9.4.2). ]
  There
  shall not be a static and a nonstatic member function with the same name and
  the same parameter types (13.1). A static member function shall not be
  declared const, volatile, or const volatile."
  (Removed the sentence "A static member function shall not be virtual.")

  10.3 [class.virtual]: change the 7th paragraph to read:
  "[Note: the virtual specifier implies membership, so a virtual function
  cannot be a nonmember (7.1.2) function. A virtual function declared in one
  class can be declared a friend in another class.]"
  (Removed the sentence "Nor can a virtual function be a static member,
  since a virtual function relies on a specific object for determining
  which function to invoke.")

  12.5 [class.fre]: change 10th paragraph to:
  "Member allocation and deallocation functions cannot be virtual. However,
  the deallocation function actually called is determined by the destructor
  actually called, so if the destructor is virtual the effect is the
  same. [Example: ..."
  (First sentence was "Since member allocation and deallocation functions
  are static they cannot be virtual.")

  ?? 5.2.4 [expr.ref] I'm unsure about 4th paragraph:
  2nd bullet (with E1.E2 syntax where E2 is a static member function)
  says that E1.E2 is an lvalue.
  4th bullet (same syntax where E2 is a non-static member function)
  says the E1.E2 is NOT an lvalue.
  (It seems to me they should both be NOT an lvalue - I don't understand
  the reason or significance of it being an lvalue in #2, so I don't
  understand whether a change is needed here for virtual static.)

  ?? 10.3 [class.virtual] I'm unsure about the 6th paragraph - it doesn't
  seem entirely accurate for static virtuals, but this inaccuracy is
  removed by 10.3 paragraph 12. (I think this is equally true
  for nonstatic virtuals.)

  With these changes, the rest of the draft adequately describes the
  intended behavior. Specificially, these need no changes:
  	5.2.2 [expr.call] paragraphs 1,2
  	5.2.4 [expr.ref] 2,6
  	5.3.1 [expr.unary.op] 2
  	7.1.1 [dcl.stc] 4
  	7.1.2 [dcl.fct.spe] all (as ammended)
  	8.3 [dcl.meaning] 1
  	9.2 [class.mem] 10
  	9.5 [class.static] all (as ammended)
  	10.2 [class.member.lookup] 5
  	10.3 [class.virtual] all (as ammended)
  	10.4 [class.abstract] all
  	11.6 [class.access.virt] all
  	12.1 [class.ctor] 4
  	12.6.2 [class.base.init] 7
  	12.7 [class.cdtor] 3
  	12.8 [class.copy] 6
  	13.1 [over.load] 2





  Narrative Rationale
  ------------------------------------------------------------------------------
  Please note that I'm not a naive newbie as I'm sometimes assumed to be when
  I memtion this topic. I've a computer engineering degree, 15 years
  in software including 7 years intensive in C++ as a technical lead
  on a 1M LOC project. (That shows I'm not a newbie - but it doesn't
  show that I'm not naive!)

  I view this not as an extension, but rather as a simplification.
  It is probably the only change request you've received that leaves the
  draft smaller and adds capability to the language.

  I could find no clear justification for the prohibition in the ARM, D&E
  or the April Draft. I infered the thinking was along the lines of
  "virtual functions require an object, static member functions are not
  associated with an object, therefore these are mutually exclusive".

  However, static member functions may be called on an object using
  class member access syntax (. or ->), in which case the static type
  (as opposed to dynamic type) is used.
  For example:

  	// Example 1
  	struct B { static void f(); /* ... */ };
  	void X(B*p) { p->f(); /* same as B::f(); */ }

  I interpret this as performing an operation on the class of the object
  pointed to by p. I believe it is logical to assume that this call
  could also be virtually dispatched. For example:

  	// Example 2
  	// Illegal in draft. This is what I want.
  	struct B { static virtual void f1(); virtual void f2(); };
  	struct D : public B { static void f1(); void f2(); };
  	void X(B*p) { p->f1(); p->f2(); }
  	main() {
  		B *p = new B;
  		X(p); // X() calls B::f1() and  B::f2()
  		p = new D;
  		X(p); // X() SHOULD call D::f1() and D::f2()
  	}

  The symmetry should be apparent. So, I believe this is natural and
  consistent.
  (And I believe that should be sufficient basis for allowing virtual static.)

  But why would anybody want to use virtual static?
  If wanted them (tried to use them) on several occaisions:
  	object factories (most recently)
  	per class registries with polymorphic behavior
  	help & documentation features (class specific in a class hierarchy)
  	built in test/debug capabilities
  	problems that RTTI now solves
  Specific other uses (suggested by Ulf Schuenemann) include: DynamicSizeof(),
  MemMoveable(), MemCopyable(), and NumInstances().
  (Also, adding virtual behavior to the class operator delete [class.fre -
  section 12.5] might have been less creative if static virtuals were
  legal at that time. Have there been other creative work-arounds to the
  lack of static virtual?))


  There is an obvious work around, but I consider it unreliable on large
  projects: instead of a single static virtual function,
  implement the static function and also a virtual function (of a slightly
  different name) that calls the static function. I'm not concerned about
  the extra function call as much as 1) explaining this work-around to
  new programmers is hard. They always say "huh?". and 2) the hard-to-find
  bug when someone calls the static function where they should have called
  the virtual function (note: we have times where we also want
  to use the class::function() syntax - so we don't make the static function
  private - even if we made it private, we could make the same mistake
  from a member function). An example:

  	// Example 3
  	// Work-around (with a bug)
  	struct B {
  		static void f();
  		virtual void Vf() { f(); };
  	};
  	struct D : public B {
  		static void f();
  		virtual void Vf() { f(); };
  	};
  	void X(B *p) {		// p* might be B or D
  		B::f();	// OK
  		D::f();	// OK
  		p -> Vf();		// OK
  		p -> f();		// Hard-to-find programmer error:
  					// WRONG if p* is a D!
  					// B::f() is called, we
  					// wanted D::f()
  	}

  INFORMALLY, here's a Truth Table that indicates when a static virtual
  would have "virtual" behavior:
  				Calling syntax:
  		B::f()		p->f() or r.f()		f()
  Called from:	================================================
  Outside of class| 1			2		3
  static memfct	| 1			2		4
  nonstatic memfct| 1			2		5
  where B::f() is a static virtual, and B *p, B r&.
  1: unambiguous - same as static non-virtual.
  2: dispatch virtually through the object (similarly to a nonstatic virtual)
  3: ill-formed, class is not known.
  4: same as static non-virtual (call f() for the class of the memfct)
  5: dispatch virtually through (*this) (same as nonstatic virtual)
  Again, this is natural and consistent with existing aspects of C++.


  A couple of asides:
  1) I heartily endorsed the concept of const member functions when they
  "appeared" (although several of my programmers objected - too confining,
  too much hassle or "false" protection). It both forced the developers to
  think more up-front about their code design and it allowed the compiler
  to assist maintenance programmers from making "intent" mistakes by
  preventing them from changing data they shouldn't (bugs which were
  difficult to find). As a const member function can't "change" the
  object, I view a static member function as not being able to even
  "examine" the object (i.e. there is no object). However, developers
  needing "virtual" capability short-circuit this by making what should
  be static member functions nonstatic (see "Design Patterns" -
  Gamma et. al page 115 for an example). Thus, there is less protection
  against maintenance programmers using data they shouldn't. Having
  static virtual would allow us to make a clean distinction as to
  when/why to use static member functions.

  2) I remember the first time I heard of an inline virtual
  function. I thought that made no sense since inline is inherently a
  compile-time (early) binding and virtual implied a run-time (late)
  binding. I had to play with it a bit to understand the value.
  I think static virtual is similar. At first it doesn't make sense
  (virtual requires an object, static doesn't have an object).
  But if viewed as allowing late binding (through an object) to the
  appropriate static member function, it does make sense.




  D&E "Criteria"
  ------------------------------------------------------------------------------

  > [1]	Is it precise? (Can we understand what you are suggesting?) Make
  > 	a clear, precise statement of the change as it affects the current
  > 	draft of the language reference standard.

  >From the above, I trust you'll understand what I'm suggesting.
  I can't be certain that I didn't miss any neccessary changes in the draft.

  > [a]	What changes to the grammar are needed?

  Identified above.

  > [b]	What changes to the description of the language semantics are needed?

  Identified above.

  > [c]	Does it fit with the rest of the language?

  Yes - C++ has done a good job of preventing coupling between
  orthogonal features. I view "static" and "virtual" as orthogonal.
  See Example 3 above, as well as the 1st "aside".

  > [2]	What is the rationale for the extension? (Why do you want it, and why
  > 	would we also want it?)

  > [a]	Why is the extension needed?

  (Not an extension - we're taking a restriction out. I like "simplification".)
  See above. Basically, I view it as useful. I view the
  present prohibition as un-necessary.

  > [b]	Who is the audience for the change?

  There is limited direct support for this change (see "From comp.std.c++",
  below).
  However, this is a general purpose change. Possibly useful to a significant
  minority of programmers who currently use both virtual functions and
  static functions.
  Useful to programmers who segregate "class methods" from "instance methods"
  and are looking for polymorphic "class methods" (thus, I see wide-spread
  OO potential - but this hasn't materalized during discussions on the net).



  > [c]	Is this a general-purpose change?

  Yes!

  > [d]	Does it affect one group of C++ language users more than others?

  Unsure. (I believe the usefullness of virtual static is more common on
  larger designs where more effort may be spent on isolating class from
  object behavior. But this is an unsupportable opinion.)

  > [e]	Is it implementable on all reasonable hardware and systems?

  Yes.

  > [f]	Is it useful on all reasonable hardware and systems?

  Yes.

  > [g]	What kinds of programming and design styles does it support?

  Factory methods.  Object - oriented: supports polymorphic behavior on
  classes (not just on objects).

  > [h]	What kinds of programming and design styles does it prevent?

  No changes from the Draft without this change.

  > [i]	What other languages (if any) provide such features?

  I have not used any that do. I understand the ObjectPascal (Delphi)
  supports this.

  > [j]	Does it ease the design, implementation, or use of libraries?

  Unsure.

  > [3]	Has it been implemented? (If so, has it been implemented in the exact
  > 	form that you are suggesting; and if not, why can you assume that
  > 	experience from "similar" implementations or other languages will
  > 	carry over to the features as proposed?)

  No (not to my knowledge.)

  > [a]	What effect does it have on a C++ implementation?

  > [i]	on compiler organization?

  Not sure (expect minimal effect).

  > [ii]	on run-time support?

  Not sure (expect no effect).

  > [b]	Was the implementation complete?

  Not implemented.

  > [c]	Was the implementation used by anyone other than the implementer(s)?

  N/A

  > [4]	What difference does the feature have on code?

  > [a]	What does the code look like without the change?

  See Example 3 above. Typically the code has 2 similar member
  functions - one is virtual, the other is static. The virtual simply calls
  the static.

  > [b]	What is the effect of not doing the change?

  I'll whimper about C++ growing without polishing the details. (But I'll
  get over it.) Also, I'll have to give an occaisional explanation to
  maintenance programmers on why I didn't code it more simply.

  > [c]	Does use of the new feature lead to demands for new support tools?

  No.

  > [5]	What impact does the change have on efficiency and compatibility with
  	C and existing C++?

  No impacts on efficiency or compatibility!

  > [a]	How does the change affect run-time efficiency?

  > [i]	of the code uses the new feature?

  No worse then a non-member virtual function.

  > [ii]	of code that does not use the feature?

  No change.

  > [b]	How does the change affect compile and link times?

  Negligible.

  > [c]	Does the change affect existing programs?

  > [i]	Must C++ code that does not use the feature be recompiled?

  No.

  > [ii]	Does the change affect linkage to languages such as C and Fortran?

  No.

  > [d]	Does the change affect the degree of static or dynamic checking
  >	possible for C++ programs?

  No.

  > [6]	How easy is the change to document and teach?

  > [a]	to novices?

  Trivial -  people assume they can do virtual static until they try it.
  It's harder to teach them about the Draft as it is today.

  > [b]	to experts?

  I believe it is difficult to teach a C++ expert anything. ;-)

  > [7]	What reasons could there be for not making the extension? There will
  > 	be counter-arguments and part of our job is to find and evaluate
  > 	them, so you can just as well save time be presenting a discussion.

  I know of no technical reason nor language usability or consistency reason
  why this "extension" should be made. I did use comp.std.c++ to attempt
  to find such reasons.
  Most counter arguments I've heard are naive - they assume a contradiction
  between virtual (requires an object) and static (no object).

  There has also been the argument that it is "too late in the game" for
  this change. I'm assuming that is untrue (for relatively minor issues
  such as this), otherwise this "public comment" period would be an
  insincere gesture.

  > [a]	Does it affect old code that does not use the construct?

  No

  > [b]	Is it hard to learn?

  No

  > [c]	Does it lead to demands for further extensions?

  Of course!
  Discussion of static virtual generally leads to 2 related topics:
  1) ability for a base constructor to call a more derived
  static virtual member function and
  2) static virtual data members (and possibly even nonstatic virtual
  data members).

  I now think these should NOT be standardized now. Although #1 makes
  sense from a programmer's perspective, it is not compatible with the
  way (I believe) many implementations manage the vtable during execution
  of base constructors.  Thus, there would be significant added
  implementation complexity unwarrented by the current demand for this feature.

  #2 also makes sense from a programmer's perspective (and possibly also
  from an implementer's). There have been convincing arguments that static
  virtual data members could readily implemented into a class's
  vtable (assuming that implementation) and that most other issues, such
  as name resolution, etc. have already been worked out for virtual and
  static members. However, I've seen no attempt to identify the
  relevant sections of the draft. There are also (possibly naive) concerns
  that this change might also have unforeseen semantic issues - and therefore
  is probably too risky to standardize at this time.

  > [d]	Does it lead to larger compilers?

  Not sure. But certainly not significantly larger. (Might be smaller - they
  have 1 less error message!)

  > [e]	Does it require extensive run-time support?

  None.

  > [8]	Are there

  > [a]	Alternative ways of providing a feature to serve the need?

  Other then the hokey double function approach at the user level
  (Example 3), I'm not aware of any reasonable alternative.

  > [b]	Alternative ways of using the syntax suggested?

  Not applicable.

  > [c]	Attractive generalizations of the suggested scheme?

  This is a generalization.






  >From comp.std.c++
  ------------------------------------------------------------------------------
  In an attempt to determine the merit of the static virtual concept, and
  to determine if there was any support, I've used comp.std.c++ as
  a forum to explore this issue.
  Over the past few weeks there have been about 25 postings regarding
  static virtual, by about 15 different posters. Threads diverged along
  a number of tangents.

  Regarding a posting I made requesting specific input for
  this "public comment" I received 10 replies (9 e-mail, 1 posting that
  wasn't e-mailed). Responses originated from 4 countries, and
  judging from the e-mail addresses, 9 companies or sites.
  I requested opinions on
  	1) static virtual member functions:
  		8 in favor, 2 opposed (note, 1 response was in-favor
  		of this as a pre-requesite for #2, I'm not sure if his
  		support is independent of #2)
  	2) also allowing the base constructor to call the most derived
  		static virtual function:
  		5 in favor, 4 opposed, 1 undecided
  	3) also supporting static virtual data members:
  		2 in favor (1 strongly, 1 weakly), 6 opposed, 2 uncertain
  		or unstated)
  (the counts exclude my postion which is now for #1 only).
  The period of this very unscientific survey was June 29 through July 4
  (a relatively short period considering holidays and vacations).

  =========================================================================

  Public Review Comment # T21

  T21.1 Core
  T21.2 Library

  _____________________________________________________________________________
  Date: 5 July 1995

  From: ISO/IEC JTC1/SC22/WG14 (programming language C)

  To:   ISO/IEC JTC1/SC22/WG21 (programming language C++)

  Subject: Review of C++ draft presented for CD balloting


  INTRODUCTION

  WG14 continues to provide useful feedback to WG21 on the draft
  C++ Standard submitted for balloting as a Committee Draft.
  As with our review during the CD registration ballot (1 February
  1995), it was our hope and expectation that we could supply at
  this stage a cogent list of issues whose resolution would ensure
  maximum compatibility between our two closely related languages.
  Given the current state of the C++ draft, however, that important
  goal remains elusive:

  * Substantial features still have no accompanying semantic
  description. The discussion of locales (clause 22), for example,
  is of particular interest to the C community and remains sorely
  lacking in explanatory detail.

  * All too many substantive changes have been made that are not
  reflected in the resolutions published with the minutes of WG21
  meetings. Change bars are too numerous to provide any guidance
  to areas that have suffered real change. It is thus hard to have
  any faith that portions of the document that have been nominally
  stable are truly left unchanged.

  * The review period is woefully short. Many members of WG14 had
  only a few weeks to review a document with numerous changes since
  the last review.

  * Many statements obviously intended as normative are in Notes
  subclauses, which are said to be non-normative. Conversely, quite
  a bit of commentary still masquerades as normative text, albeit
  largely toothless.

  * The number of typographical errors and lurches in style continue
  to show that the document is nowhere near ready for the precise review
  required to determine whether compatibility between C and C++
  has been adequately safeguarded.

  As with our previous review, we supply here a simple compendium
  of comments made by various members of WG14. If the editing process
  continues past the July 1995 meeting -- as we fully expect -- WG14
  will endeavor to supply additional comments as time permits.
  And as always, we stand ready to supply additional guidance and
  eview, to ensure that C and C++ remain ``as close as possible,
  but no closer.''

  --------------------------------

  T21.1 Core

  --------------------------------

  UK Comments on C++ CD for Public Review

  (I am afraid we have hardly scratched the surface.)

  Clause 1.1

  Paragraph 2, last sentence.  Delete this sentence and Annex C.1.2.
  This is the first standard for C++, what happened prior to 1985 is
  not relevant to this document.

  Clause 1.2

  Paragraph 1, change "ISO/IEC 9899:1990, C Standard" to
  "ISO/IEC 9899:1990 Programming Languages -- C"

  Paragraph 1, change "ISO/IEC 9899:1990/DAM 1, Amendment to C Standard" to
  "ISO/IEC:1990 Programming languages -- C AMENDMENT 1: C Integrity"

  Add year of current publication of ISO/IEC 2382
  Clause 1.3

  Paragraph 1, multibyte character.  Last sentence.  What is the basic
  character set?  Is it the basic source character set or basic
  execution character set (see clause 5.2.1 of ISO 9899)?  There is
  an index refence for basic execution character set to this clause.

  Also need to add definitions of the basic execution and basic source
  character set.  See ISO 9899, Clause 5.2.1.

  Paragraph 1, undefined behaviour.  ISO 9899 states that "Undefined
  behaviouris otherwise indicated in this International Standard by the
  words "undefined behaviour" or by the omission of any explicit definition
  of behaviour".

  The C++ standard should also adopt the rule that omission of explicit
  defintion of behaviour results in undefined behaviour.

  Paragraph 1, well-formed program.  Other standards use the term
  Conforming to describe this concept.  The C++ standard should follow
  this precedent.  It should also introduce the concept of Strict
  Conformance, that is a program that contains no undefined, implementation
  defined or unspecified behaviours.

  Clause 1.5, paragraph 1, second sentence.  Contains a use of the
  term "basic execution character set".  See previous discussion.

  Clause 1.8, paragraph 4.  Need to include text stating that the standard
  imposes no requirements on the behaviour of programs that contain
  undefined behaviour.

  Clause 1.8, paragraph 9, second sentence.  What is a "needed side-effect"?
  This paragraph, along with footnote 3 appears to be a definition
  of the C standard "as-if" rule.  This rule should be defined as such.

  Clause 2.1, phase 8, first sentence.  Change "The translation units
  that will form a program are combined." to "The translation units
  are combined to form a program."

  Clause 2.2, paragraph 1.  Delete and replace with wording from
  C standard.  "All occurrences in a source file of the following
  sequences of three characters (called trigraph sequences) are replaced
  with the corresponding single character.  No other trigraph sequence
  exists.  Each ? that does not begin one of the above trigraphs listed
  above is not changed."

  Clause 2.3, paragraph 3, first sentence.  Change "... lexically analsysed
  ..."
  to "... parsed ...".  To agree with wording in C standard.

  Clause 2.3, paragraph 3, last sentence.  Delete ", even if that would
  cause further lexical analysis to fail".  To agree with existing, clear
  wording in C standard.

  Clause 2.4.  This is a gratuatous difference from the Addendum
  to the C standard with no technical merit.  It should be deleted
  and replaced by the text from the Addendum.

  Clause 2.8, paragraph 3.  Reserving identifiers containing a double
  underscore is overly restrictive.  Identifiers starting with
  double underscore should be reserved.

  Clause 2.9.1, paragraph 1.  This is a clumsy rewrite of the description
  in Clause 6.1.3.2 of the C standard.  Replace by the text contained
  in the two paragraphs of the Description in Clause 6.1.3.2.

  Clause 2.9.1, paragraph 2.  This is a clumsy rewrite of the
  semantics in Clause 6.1.3.2 of the C standard.  Replace by the
  text contained in the two paragraphs of the Semantics in Clause 6.1.3.2.

  Clause 2.9.2, paragraph 1, second sentence.  What is "the machine's
  character set"?  Is this the basic source character set that we have
  forgotten to define?  Suggest that the wording from C standard, Clause
  6.1.3.4, Semantics, first paragraph be used (it contains the
  important concept of mapping).

  Clause 2.9.2, paragraph 2.  Suggest that C standard, Clause 6.1.3.4,
  Semantics, second paragraph be used as the basis of a rewrite of this
  paragraph.

  Clause 2.9.2, paragraph 3.  Suggest that C standard, Clause 6.1.3.4,
  Description, paragraph 2, 3, 4, and 5 be used as the basis of a
  rewrite of this paragraph.

  Clause 2.9.2, paragraph 4.  Ditto comment on paragraph 3.

  Clause 2.9, paragraph 1.  Suggest that this be replaced by C standard
  Clause 6.1.3.1, Description, paragraph 1.  Otherwise the term "missing"
  should be replaced by "ommitted".

  Clause 2.9.4.  Suggest that paragraph 1, 2 and 3 be replaced by
  C standard, Clause 6.1.4, all paragraphs in Description and Semantics.

  Clause 2.9.4, paragraph 4.  Delete.  The size of a string is
  not equal to the number of characters it contains.  The \" rule
  is already covered by the text from the C standard.  The first paragraph
  belongs in an introductory text to the language.

  Clause 5.16, syntax rule.  Change "assignment-expression" to
  "conditional-expression" to agree with the C standard, ISO 9899
  Clause 6.3.15

  Page 32 Para 9

  This states :
  Types bool, char, wchar_t, and the signed and unsigned integer types are
  collectively called integral types. 27) A synonym for integral type is
  integer type.

  ISO 9899 does not include wchar_t as a member of the integral types, this
  should at least be noted in Annex C, and does raise a number of
  compatability issues

  Page 84 Para 5

  The underlying type of an enumeration is an integral type, not
  gratuitously
  larger than int

  Is this meant to be a requirement on an implementation ?

  if so then the requirement should be stated positively.
  i.e. an enumeration is an integral type that can represent all enumerator
  values .... otherwise remove the not gratuitously ...

  1.7 Processor compliance para 2
  typo -diagnosable errors repeated

  Page 6 para 18
  the word builtin needsd a hypen i.e built-in

  Paragraph 3.3.4 Page 20
  Scope

  File 1
  // First file
  // declare i in global namespace as per page 20 of draft
  // and has external linkage

  int i=5;

  File 2
  //Second file

  static int i = 10 ;  // declare i in global namespace with internal
  linkage
  int y = ::i ;   // What is the value of y
                  // does :: resolve linkage to external or internal ??

  void f(void)
  {
          int i =6;
          int j =::i; // Global namespace i internal or external

  }


  If an implementation is required to accept both

  int main(){}

  and

  int main(int argc, char * argv[]){}

  Is it permitted to have a prototype of both forms visible ?

  int main();
  int main(int, char **);

  If not is a disgnostic required nn this case.

  Page 77

  The following two statements appear to contradict each other

  The inline specifier is a hint to the implementation that inline
  substitution of the function body is to be preferred to the usual
  function call implementation.  The hint can be ignored.

  The above statement clearly indicates that inline can be ignored however
  the draft goes on to state:

  A function (8.3.5, 9.4, 11.4) defined within the class definition "is"
  inline.

  Is an implementation free to ignore the inline within a class definition ?

  Page 45 para 7 [expr.call]

  This section describes the promotions prior to a function call and refers
  to section 4.5 (integral promotions), however section 4.5 refers to
  promotion of wchar_t and bool, paragraph 7 remains silent on wchar_t and
  bool leaving a question over whether promotion of these takes place prior
  to the function call.

  ------------------------------

  The following are points directly relating to C.

  Clause 3.9, paragraph 6, last sentence.  In ISO 9899 an incomplete
  type is not an object type (Clause 6.1.2.5, first paragraph).  Defining
  an "incompletely-defined object type" is a needless incompatibility
  with ISO 9899.  Use another term.

  Clause 3.9, paragraph 7, last sentence.  ISO 9899 allows a typedef
  declaration of an array of unknown size to be later completed for
  a specific object (Clause 6.5.7, example 6).  C++ should also
  allow such a usage.  Disallowing this construct is a needless
  incompatibility.

  ---------------------------------

  !!! indicates meatier comments.

  3.6.2.  The latitude with which static initialization might occur is
  problematic for use of the floating-point environment, viz. the
  floating-point
  exception flags and rounding direction modes required by IEC559.  The
  sequence
  { clear-overflow-flag, compute, test-overflow-flag } would be defeated if the
  implementation chose to execute some overflowing static initializations
  between the clear and test.  The sequence { set-special-rounding, compute,
  restore-usual-rounding } could affect the results of static initializations
  the implementation chose to execute between the set and restore.  In order to
  support the floating-point environment, some implementations, depending on
  their initialization model, might need to insulate static initialization with
  say { save-FP-environment, set-default-FP-environment,
  execute-initializations, restore-FP-environment }.  A note to this effect
  would be helpful.

  3.9.1, P10, Box 21.  Yes, say "at least as much range and precision".  Both
  are desired, and one doesn't imply the other.

  5, P4.  The first sentence may not be clear.  I assume "where the operators
  really are" means the rearrangement in question would not change values.
  Better would be to disallow rearrangement (except by the as-if rule).
  "Rearrangement" is better than "regrouping", as the distributive law is
  problematic too.

  !!! 5, P12.  There's no mention of license for wide evaluation of floating
  expressions, as in 3.2.1.5 of the C standard.  Wide evaluation is needed by
  the host of systems based on wide registers.

  --------------------------------

  T21.2 Library

  --------------------------------

  17.3.1.1,  P10, Table 15.  Typo: unititialized_fill

  17.3.3.1.2,  P1.  This seems to say that a header can optionally declare or
  define any names it wishes.  This statement may have been taken out of
  context
  from the C standard, where, I thought, the optional reserved names were
  confined to those in the subsequent bullets.

  17.3.3.2,  P1.  Sentence is difficult to parse.

  17.3.4.2,  P1.  Footnote says masking macros are disallowed.  Why disallow
  them?

  !!! 17.  Assuming wide expression evaluation is allowed, math functions
  should
  be able to have return types appropriate to the implementation's expression
  evaluation method.  E.g. if the minimum evaluation format is double, then cos
  should have the prototypes
    double cos(float);
    double cos(double);
    long double cos(long double);
  (Note this doesn't affect signatures.)

  17.3.4.8,  P3, Box 70.  I think it's right to not require C functions to
  throw
  exceptions, but why prohibit it?

  18.2.1.1.  Is tinyness_before actually useful for any programming task?
  Being
  in the interface makes the diligent programmer worry about whether she needs
  to consider it.  The IEEE 754 (IEC 559) standardization group regarded it as
  an implementation option that didn't matter to the user.

  18.2.1.2, P23, 27.  Footnote says these are equivalent to xxx_MIN_EXP and
  xxx_MAX_EXP, but their definitions don't imply that.  Better to use the same
  wording as in the C standard.

  18.2.1.2, P23, 25, 27, 29.  These refer to "range", which is intended to
  imply
  normalized.  "Range of normalized floating-point numbers", as in the C
  standard, would avoid the ambiguity.

  18.2.1.2, P61.  round_style would be more useful if its value reflected the
  current execution-time rounding style, which can be changed dynamically on
  most systems, including all IEC559 ones.

  18.2.1.4, P2.  Example is inconsistent in that is_iec559 is true but
  has_denorm is false -- IEC559 requires denorms.

  19.1.  The hierarchy of exceptions is confusing.  (1) What are the
  differences
  between domain_error, invalid_argument, and out_of_range?  (2) out_of_range
  and range_error sound like the same thing but aren't.  (3) In mathematics
  (though not the C standard), domain refers to argument values and range to
  return values, but here out_of_range refers to argument values.  (4) How do
  they map to the IEC559 exceptions (invalid, overflow, underflow, div-by-zero,
  and inexact)?

  19.1.  I believe (and hope) there's not a requirement that builtin operators
  on builtin types or standard math functions throw any of these exceptions,
  but
  a reader might leap to the conclusion that they do.

  !!! 26.2.  The complex library provides a subset of the capabilities one
  might
  expect from builtin complex types.  A description of what capabilities are
  and
  are not supported would be very helpful.  What conversions?  Which among
  complex<int>, complex<long>, complex<float>, and complex<double> have
  implicit
  conversions?  What (mixed mode) operations?  Do integer and complex operands
  mix (e.g. complex_z * 2)?  Is double_complex_z * 2.0L OK?  Without this
  description the reader must infer from the overloading rules.  (It appears
  there are no implicit conversions from complex to real nor from wider to
  narrower among complex<long double>, complex<double>, and complex<float>,
  which presumably allows for automatic "promotions" from real to complex and
  from narrower to wider complex types.  Saying so much -- whatever is correct
  --  would be helpful.)

  !!! 26.2  In reviewing the complex library I'm further confounded by not
  being
  able to try it.  It uses member templates, which aren't implemented in either
  of the two compilers I have access to.  Are there enough implementations of
  this?

  26.2 (and elsewhere).  The lack of rationale makes review more difficult.

  26.2, P1.  Typo in the second divide operator.

  26.2.1.  What are the requirements for type X?

  !!! 26.2.2.  Compound assignments should be overloaded for real operands.
  This is CRITICAL for consistency with IEC559 and for efficiency (see section
  2.3.6 of "Complex C Extensions", Chapter 6 of X3J11's TR on Numerical C
  Extensions), particularly since the binary operators are defined in terms of
  the compound assignments.  complex_z *= 2.0 must not entail a conversion of
  2.0 to complex.

  26.2.2.  Why initialize re and im to 0?.

  26.2.3.  How do the default arguments like T re = T() apply to builtin types
  like int?

  26.2.4.  The class declarations for the compound assignments use member
  templates, but they don't show up here.  Likewise the complex(const
  complex<X>&) constructor is missing.

  !!! 26.2.5.  Definitions for binary operators refer to compound assignments,
  but compound assignments aren't declared for complex<T> op= T.  This is a
  deficiency in the compound assignments (see above).  Also the semantics are
  wrong for T op complex<T>, as they entail a conversion of T to complex<T>
  (see
  above).

  26.2.5.  For ==, typo:  lhsP.real

  26.2.5.  For ==, the Returns and Notes parts are awkward.

  26.2.5.  For !=, typo in Returns part.

  26.2.6.  abs is missing.

  26.2.6.  Can't review the two TBS.

  26.2.6.  I  believe the term "norm" commonly refers to the square root of the
  squared magnitude (i.e. abs), and not the squared magnitude.  Is a function
  for the squared magnitude needed?  Note that the squared magnitude can be
  computed from abs with only deserved over/underflow, but not vise versa.

  26.2.6.  Typos in argument list for polar.

  26.2.7.  I don't think atan2 should be overloaded for complex arguments?  How
  would it be defined?

  26.2.7.  log10(z) is easily computed as log(z)/log(10.0), so isn't really
  necessary.

  !!! 26.2.7.  Branch cuts and ranges need to be specified for functions.  See
  section 3 of "Complex C Extensions", Chapter 6 of X3J11's TR on Numerical C
  Extensions.

  26.5.  There's no long double version of ldexp.

  26.5.  The float version of modf is out of alphabetical order.

  26.5.  pow doesn't accommodate mixed mode calls.  E.g. pow(2.0f, 3.0) is
  ambiguous, matching both pow(float,float) and pow(float,int).  pow(2.0, 3L)
  is
  ambiguous too.  A description (clearer than the overloading rules) would be
  helpful.  Maybe more overloads are desirable.

  26.5.  New overloads make math functions ambiguous for integer arguments,
  e.g.
  atan(1) would be ambiguous.  C++ would be more restrictive than C in this
  respect.  Of course, more overloads could solve the problem.

  !!! 26.5.  The functions in <fp.h> and <fenv.h>, specified in "Floating-Point
  C Extensions", Chapter 5 of X3J11's TR on Numerical C Extensions, support a
  substantially broader spectrum of numerical programming.

  ---------------------------------

  17.3.1.3:
  A freestanding implementation doesn't include <stdexcept>,
  which defines class exception, needed by <exception>.
  Should probably move class exception to <exception>.

  17.3.3.1:
  A C++ program must be allowed to extend the namespace std if only
  to specialize class numeric_limits.

  17.3.4.1:
  Paragraph 4 is a repeat.

  18.2.1:
  float_rounds_style should be float_round_style (correct once).

  18.2.1.1:
  Paragraph 2 is subsumed by the descriptions of radix, epsilon(),
  and round_error(). Should be removed here.

  18.2.1.1:
  Paragraph 3 is repeated as 18.2.1.2, paragraph 50, where it belongs.
  Should be removed here.

  18.2.1.1:
  Should say that numeric_limits<T> must be able to return T(0).
  Should say that round_style defaults to round_indeterminate,
  not round_toward_zero.

  18.2.1.2:
  denorm_min() does *not* return the minimum positive normalized value.
  Should strike the mention of this function in paragraph 2.

  18.2.1.2:
  Paragraph 22 must supply a more precise definition of ``rounding error.''

  18.2.1.2:
  Paragraph 23 must replace ``is in range'' with
  ``is a normalized value''.

  18.2.1.2:
  Paragraph 25 must replace ``is in range'' with
  ``is a normalized value''.

  18.2.1.2:
  Paragraph 27 must replace ``is in range'' with
  ``is a finite value''.

  18.2.1.2:
  Paragraph 29 must replace ``is in range'' with
  ``is a finite value''.

  18.2.1.2:
  In paragraph 41, ``flotaing'' should be ``floating''.

  18.2.1.3:
  Semantics must be specified for enum float_round_style.

  18.5.1:
  type_info::operator!=(const type_info&) is ambiguous
  in the presence of the template operators in <utility>, and it is
  unnecessary. It should be removed.

  18.6.1.1:
  Paragraph 1 incorrectly states that bad_exception is thrown by the
  implementation to report a violation of an exception-specification.
  Such a throw is merely a permissible option.

  18.7:
  There are five Table 28s.

  19.1.1:
  exception(const exception&) should not be declared with the
  return type exception&. (Error repeated in semantic description.)

  20.1:
  Allocators are described in terms of ``memory models'' which is an
  undefined concept in Standard C++. The term should be *defined* here
  as the collection of related types, sizes, etc. in Table 33 that
  characterize how to allocate, deallocate, and access objects of
  som  managed type.

  20.1:
  Paragraph 3 talks about ``amortized constant time'' for allocator
  operations, but gives no hint about what parameter it should be
  constant with respect to.

  20.1:
  a.max_size() is *not* ``the largest positive value of X::difference_type.''
  It is the largest valid argument to a.allocate(n).

  20.1:
  Table 33 bears little resemblance to the currently accepted version
  of class allocator (though it should, if various bugs are fixed, as
  described later.) Essentially *every* item in the `expression' column
  is wrong, as well as all the X:: references elsewhere in the table.

  20.3:
  binder1st is a struct in the synopsis, a class later.
  Should be a class uniformly, like binder2nd.

  20.3.5:
  class unary_negate cannot return anything. Should say that its
  operator() returns !pred(x).

  20.3.6.1:
  binder1st::value should have type Operation::first_argument_type,
  not argument_type.

  20.3.6.3:
  binder2nd::value should have type Operation::second_argument_type,
  not argument_type.

  20.3.7:
  ``Shall'' is inappropriate in a footnote, within a comment, that
  refers to multiple memory models not even recognized by the Standard.

  20.4:
  return_temporary_buffer shouldn't have a second (T*) parameter.
  It's not in STL, it was not in the proposal to add it, and
  it does nothing.

  20.4.1:
  allocator::types<T> shows all typedefs as private.
  They must be declared public to be usable.

  20.4.1:
  It is not clear from Clause 14 whether explicit template member
  class specializations can be first declared outside the containing
  class. Hence, class allocator::types<void> should probably be declared
  inside class allocator.

  20.4.1:

  The explicit specialization allocator::types<void> should include:
      typedef const void* const_pointer;
  It is demonstrably needed from time to time.

  20.4.1:
  Footnote 169 should read ``An implementation,''
  not ``In implementation.''

  20.4.1.1:
  allocator::allocate(size_type, types<U>::const_pointer) has no
  semantics for the second (hint) parameter.

  20.4.1.1:
  allocator::allocate(size_type, types<U>::const_pointer) requires
  that all existing calls of the form A::allocate(n) be rewritten
  as al.allocate<value_type, char>(n, 0) -- a high notational
  price to pay for rarely used flexibility. If the non-template form
  of class allocator is retained, an unhinted form should
  be supplied, so one can write al.allocate<value_type>(n).

  20.4.1.1:
  allocator::allocate(size_type, types<U>::const_pointer) should
  return neither new T nor new T[n], both of which call the default
  constructor for T one or more times. Note that deallocate, which
  follows, calls operator delete(void *), which calls no destructors.
  Should say it returns operator new((size_type)(n * sizeof (T))).

  20.4.1.1:
  allocator::max_size() has no semantics, and for good reason. For
  allocator<T>, it knew to return (size_t)(-1) / sizeof (T) --
  the largest sensible repetition count for an array of T. But the
  class is no longer a template class, so there is no longer a T to
  consult. Barring a general cleanup of class allocator, at the least
  max_size() must be changed to a template function, callable as
  either max_size<T>() or max_size(T *).

  20.4.1.1:
  A general cleanup of class allocator can be easily achieved by
  making it a template class once again:
  template<class T> class allocator {
  public:
      typedef size_t    size_type;
      typedef ptrdiff_t difference_type;
      typedef T*        pointer;
      typedef const T*  const_pointer;
      typedef T&        reference;
      typedef const T&  const_reference;
      typedef T         value_type;
      pointer address(reference x) const;
      const_pointer address(const_reference x) const;
      pointer allocate(size_type n);
      void deallocate(pointer p);
      size_type init_page_size() const;
      size_type max_size() const;
      };
  The default allocator object for a container of type T would then
  be allocator<T>(). All of the capabilities added with the Nov. '94
  changes would still be possible, and users could write replacement
  allocators with a *much* cleaner interface.

  20.4.1.2:
  operator new(size_t N, allocator& a) can't possibly return
  a.allocate<char, void>(N, 0). It would attempt to cast the
  second parameter to allocator::types<void>::const_pointer,
  which is undefined in the specialization allocator::types<void>.
  If related problems aren't fixed, the second template argument
  should be changed from void to char, at the very least.

  20.4.1.2:
  If allocator is made a template class once again, this version

  of operator new becomes:
  template<class T>
      void *operator new(size_t, allocator<T>& a);

  20.4.1.3:
  The example class runtime_allocator supplies a public member
  allocate(size_t) obvoously intended to mask the eponymous
  function in the base class allocator. The signature must be
  allocate<T, U>(size_t, types<U>::const_pointer) for that to
  happen, however. The example illustrates how easy it is to
  botch designing a replacement for class allocator, given its
  current complex interface. (The example works as is with the
  revised template class allocator described earlier.)

  20.4.2:
  raw_storage_iterator<OI, T>::operator*() doesn't return ``a reference
  to the value to which the iterator points.'' It returns *this.

  20.4.3.1:
  Template function allocate doesn't say how it should ``obtain a
  typed pointer to an uninitialized memory buffer of a given size.''
  Should say that it calls operator new(size_t).

  20.4.3.2:
  Template function deallocate has no semantics. Should say that
  it calls operator delete(buffer).

  20.4.3.5:
  get_temporary_buffer fails to make clear where it ``finds the largest
  buffer not greater than ...'' Do two calls in a row ``find'' the same
  buffer? Should say that the template function allocates the buffer
  from an unspecified pool of storage (which may be the standard heap).
  Should also say that the function can fail to allocate any storage
  at all, in which case the `first' component of the return value is
  a null pointer.

  20.4.3.5:
  Strike second parameter to return_temporary_buffer, as before.
  Should say that a null pointer is valid and does nothing.
  Should also say that the template function renders indeterminate
  the value stored in p and makes the returned storage available
  for future calls to get_temporary_buffer.

  20.4.4:
  Footnote 171 talks about ``huge pointers'' and type ``long long.''
  Neither concept is defined in the Standard (nor should it be).
  This and similar comments desperately need rewording.

  20.4.4.3:
  Header should be ``uninitialized_fill_n'', not ``uninitialized_fill.''

  20.4.5:
  When template class auto_ptr ``holds onto'' a pointer, is that the
  same as storing its value in a member object? If not, what can it
  possibly mean?

  20.4.5:
  auto_ptr(auto_ptr&) is supposed to be a template member function.

  20.4.5:
  auto_ptr(auto_ptr&) is supposed to be a template member function.

  20.4.5:
  auto_ptr<T>::operator= should return auto_ptr<T>&, not void, according
  to the accepted proposal.

  20.4.5.1:
  Need to say that auto_ptr<T>::operator= returns *this.

  20.4.5.2:
  auto_ptr<T>::operator->() doesn't return get()->m -- there is no m.
  Should probably say that ap->m returns get()->m, for an object ap
  of class auto_ptr<T>.

  20.4.5.2:
  auto_ptr<T>::release() doesn't say what it returns. Should say
  it returns the previous value of get().

  20.4.5.2:
  auto_ptr<T>::reset(X*) doesn't say what it returns, or that it deletes
  its current pointer. Should say it executes ``delete get()'' and
  returns its argument.

  20.5:
  The summary of <ctime> excludes the function clock() and the
  types clock_t and time_t. Is this intentional?

  21.1:
  template function operator+(const basic_string<T,tr,A> lhs,
     const_pointer rhs) should have a second argument of type
  const T *rhs.

  21.1:
  Paragraph 1 begins, ``In this subclause, we call...'' All first person
  constructs should be removed.

  21.1.1.1:
  string_char_traits::ne is hardly needed, given the member eq.
  It should be removed.

  21.1.1.1:
  string_char_traits::char_in is neither necessary nor sufficient.
  It simply calls is.get(), but it insists on using the basic_istream
  with the default ios_traits. operator>> for basic_string still has
  to call is.putback(charT) directly, to put back the delimiter that
  terminates the input sequence. char_in should be eliminated.

  21.1.1.1:
  string_char_traits::char_out isn't really necessary.
  It simply calls os.put(), but it insists on using the basic_ostream
  with the default ios_traits. char_out should be eliminated.

  21.1.1.1:
  string_char_traits::is_del has no provision for specifying a locale,
  even though isspace, which it is supposed to call, is notoriously
  locale dependent. is_del should be eliminated, and operator>> for
  strings should stop on isspace, using the istream locale, as does
  the null-terminated string extractor in basic_istream.

  21.1.1.1:
  string_char_traits is missing three important speed-up functions,
  the generalizations of memchr, memmove, and memset. Nearly all the
  mutator functions in basic_string can be expressed as calls to these
  three primitives, to good advantage.

  21.1.1.2:
  No explanation is given for why the descriptions of the members of
  template class string_char_traits are ``default definitions.''
  If it is meant to suggest that the program can supply an explicit
  specialization, provided the specialization satisfies the semantics
  of the class, then the text should say so (here and several other
  places as well).

  21.1.1.2:
  string_char_traits::eos should not be required to return the
  result of the default constructor char_type() (when specialized).
  Either the specific requirement should be relaxed or the function
  should be eliminated.

  21.1.1.2:
  string_char_traits::char_in, if retained, should not be required to
  return is >> a, since this skips arbitrary whitespace. The proper
  return value is is.get().

  21.1.1.2:
  string_char_traits::is_del, if retained, needs to specify the locale
  in effect when it calls isspace(a).

  21.1.1.3:
  Paragraph 1 doesn't say enough about the properties of a ``char-like
  object.'' It should say that it doesn't need to be constructed or
  destroyed (otherwise, the primitives in string_char_traits are
  woefully inadequate). string_char_traits::assign (and copy) must
  suffice either to copy or initialize a char_like element.
  The definition should also say that an allocator must have the
  same definitions for the types size_type, difference_type, pointer,
  const_pointer, reference, and const_reference as class
  allocator::types<charT> (again because string_char_traits has no
  provision for funny address types).

  21.1.1.4:
  The copy constructor for basic_string should be replaced by two

  constructors:
  basic_string(const basic_string& str);
  basic_string(const_basic_string& str, size_type pos,
     size_type n = npos, Allocator& = Allocator());
  The copy constructor should copy the allocator object, unless
  explicitly stated otherwise.

  21.1.1.4:
  basic_string(const charT*, size_type n, Allocator&) should be

  required to throw length_error if n > max_size(). Should say:
  Requires: s shall not be a null pointer
            n <= max_size()
  Throws: length_error if n > max_size().

  21.1.1.4:
  basic_string(size_type n, charT, Allocator&) is required to throw

  length_error if n == npos. Should say:
  Requires: n <= max_size()
  Throws: length_error if n > max_size().

  21.1.16:
  basic_string::size() Notes says the member function ``Uses
  traits::length(). There is no reason for this degree of
  overspecification. The comment should be struck.

  21.1.1.6:
  basic_string::resize should throw length_error for n >= max_size(),
  not n == npos.

  21.1.1.6:
  resize(size_type) should not have a Returns clause -- it's a void
  function. Clause should be labeled Effects.

  21.1.1.6:
  resize(size_type) should call resize(n, charT()), not
  resize(n, eos()).

  21.1.16:
  basic_string::resize(size_type) Notes says the member function
  ``Uses traits::eos(). It should actually use charT() instead.
  The comment should be struck.

  21.1.1.6:
  basic_string::reserve says in its Notes clause, ``It is guaranteed
  that...'' A non-normative clause cannot make guarantees. Since the
  guarantee is important, it should be labeled differently.
  (This is one of many Notes clauses that make statements that should
  be normative, throughout the description of basic_string.)

  21.1.1.8.2:
  basic_string::append(size_type n, charT c) should return
  append(basic_string(n, c)). Arguments are reversed.

  21.1.1.8.3:
  basic_string::assign(size_type n, charT c) should return
  assign(basic_string(n, c)). Arguments are reversed.

  21.1.1.8.4:
  basic_string::insert(size_type n, charT c) should return
  insert(basic_string(n, c)). Arguments are reversed.

  21.1.1.8.4:
  basic_string::insert(iterator p, charT c) should not return p,
  which may well be invalidated by the insertion. It should return
  the new iterator that designates the inserted character.

  21.1.1.8.4:
  basic_string::insert(iterator, size_type, charT) should return
  void, not iterator. (There is no Returns clause, luckily.)

  21.1.1.8.5:
  basic_string::remove(iterator) says it ``calls the character's
  destructor'' for the removed character. This is pure fabrication,
  since constructors and destructors are called nowhere else, for
  elements of the controlled sequence, in the management of the
  basic_string class. The words should be struck.

  21.1.1.8.5:
  basic_string::remove(iterator, iterator) says it ``calls the character's
  destructor'' for the removed character(s). This is pure fabrication,
  since constructors and destructors are called nowhere else, for
  elements of the controlled sequence, in the management of the
  basic_string class. The words should be struck.

  21.1.1.8.5:
  basic_string::remove(iterator, iterator) Complexity says ``the
  destructor is called a number of times ...'' This is pure fabrication,
  since constructors and destructors are called nowhere else, for
  elements of the controlled sequence, in the management of the
  basic_string class. The Complexity clause should be struck.

  21.1.1.8.6:
  replace(size_type pos1, size_type, const basic_string&,...) Effects
  has the expression ``size() - &pos1.'' It should be ``size() - pos1.''

  21.1.1.8.6:
  basic_string::replace(size_type, size_type n, charT c) should return
  replace(pos, n, basic_string(n, c)). Arguments are reversed.

  21.1.1.8.8:
  basic_string::swap Complexity says ``Constant time.'' It doesn't
  say with respect to what. Should probably say, ``with respect to
  the lengths of the two strings, assuming that their two allocator
  objects compare equal.'' (This assumes added wording describing
  how to compare two allocator objects for equality.)

  21.1.1.9.1:
  basic_string::find(const charT*, ...) Returns has a comma missing
  before pos argument.

  21.1.1.9.8:
  basic_string::compare has nonsensical semantics. Unfortunately,
  the last version approved, in July '94 Resolution 16, is also
  nonsensical in a different way. The description should be
  restored to the earlier version, which at least has the virtue

  of capturing the intent of the original string class proposal:
  1) If n is less than str.size() it is replaced by str.size().
  2) Compare the smaller of n and size() - pos with traits::compare.
  3) If that result is nonzero, return it.
  4) Otherwise, return negative for size() - pos < n, zero for
  size() - pos == n, or positive for size() - pos > n.

  21.1.1.10.3:
  operator!=(const basic_string&, const basic_string&) is ambiguous
  in the presence of the template operators in <utility>, and it is
  unnecessary. It should be removed.

  21.1.1.10.5:
  operator>(const basic_string&, const basic_string&) is ambiguous
  in the presence of the template operators in <utility>, and it is
  unnecessary. It should be removed.

  21.1.1.10.6:
  operator<=(const basic_string&, const basic_string&) is ambiguous
  in the presence of the template operators in <utility>, and it is
  unnecessary. It should be removed.

  21.1.1.10.7:
  operator>=(const basic_string&, const basic_string&) is ambiguous
  in the presence of the template operators in <utility>, and it is
  unnecessary. It should be removed.

  21.1.1.10.7:
  operator>= with const charT* rhs should return
  lhs >= basic_string(rhs), not <=.

  21.1.1.10.8:
  Semantics of operator>> for basic_string are vacuous.
  Should be modeled after those for earlier string class.

  21.1.1.10.8:
  Semantics of operator<< for basic_string are vacuous.
  Should be modeled after those for earlier string class.

  21.1.1.10.8:
  getline for basic_string reflects none of the changes adopted by
  July '94 resolution 26. It should not fail if a line exactly fills,
  and it should set failbit if it *extracts* no characters, not if it
  *appends* no characters. Should be changed to match 27.6.1.3.

  21.1.1.10.8:
  getline for basic_string says that extraction stops when npos - 1
  characters are extracted. The proper value is str.max_size() (which
  is less than allocator.max_size(), but shouldn't be constrained
  more precisely than that). Should be changed.

  21.2:
  There are five Table 44s.

  21.2:
  <cstring> doesn't define size_type. Should be size_t.

  22.1:
  template operator<<(basic_ostream, const locale&) as well as
  template operator>>(basic_ostream, const locale&) now have a second
  template argument (for ios traits) added without approval. While
  this change may be a good idea, it should be applied uniformly (which
  has not happened), and only after committee approval.

  22.1.1:
  locale::category is defined as type unsigned. For compatibility with
  C, it should be type int.

  22.1.1:
  class locale has the constructor locale::locale(const locale& other,
  const locale& one, category). I can find no resolution that calls
  for this constructor to be added.

  22.1.1:
  Example of use of num_put has silly arguments. First argument
  should be ostreambuf_iterator(s.rdbuf()).

  22.1.1:
  Paragraph 8 says that locale::transparent() has unspecified behavior
  when imbued on a stream or installed as the global locale. There is
  no good reason why this should be so and several reasons why the
  behavior should be clearly defined. The sentence should be struck.

  22.1.1:
  Paragraph 9 says that ``cach[e]ing results from calls to locale facet
  member functions during calls to iostream inserters and extractors,
  and in streambufs between calls to basic_streambuf::imbue, is
  explicitly supported.'' In the case of inserters and extractors,
  this behavior follows directly from paragraph 8. No need to say it
  again. For basic_streambuf, the draft can (and should) say explicitly
  that the stream buffer fixates on a facet at imbue time and ignores
  any subsequent changes that might occur in the delivered facet
  until the next imbue time (if then). (An adequate lifetime for the
  facet can be assured by having the basic_streambuf object memorize
  a copy of a locale object directly containing the facet,
  as well as a pointer to the facet, for greater lookup speed.)
  In any event, saying something ``is explicitly supported'' doesn't
  make the behavior *required.* The paragraph should be struck, and
  words added to the description of basic_streambuf to clarify the
  lifetime of an imbued codecvt facet. (More words are needed here
  anyway, for other reasons.)

  22.1.1.1.1:
  Table 46 lists the ctype facets codecvt<char, wchar_t, mbstate_t>
  and codecvt<wchar_t, char, mbstate_t> as being essential, but what
  about codecvt<char, char, mbstate_t>? Should say that this facet
  must be present and must cause no conversion.

  22.1.1.1.1:
  Table 46, and paragraph 3 following, identify the facets that implement
  each locale category (in the C library sense). But these words offer
  no guidance as to what facets should be present in the default
  locale (locale::classic()). The template classes listed each represent
  an unbounded set of possible facets. Should list the following
  explicit instantiations of the templates as being required, along

  with those explicit instantiations already listed in Table 46:
   num_get<char, istreambuf_iterator<char> >
   num_get<wchar_t, istreambuf_iterator<wchar_t> >
   num_put<char, ostreambuf_iterator<char> >
   num_put<wchar_t, ostreambuf_iterator<wchar_t> >
   money_get<char, istreambuf_iterator<char> >
   money_get<wchar_t, istreambuf_iterator<wchar_t> >
   money_put<char, ostreambuf_iterator<char> >
   money_put<wchar_t, ostreambuf_iterator<wchar_t> >
   time_get<char, istreambuf_iterator<char> >
   time_get<wchar_t, istreambuf_iterator<wchar_t> >
   time_put<char, ostreambuf_iterator<char> >
   time_put<wchar_t, ostreambuf_iterator<wchar_t> >

  22.1.1.2:
  As mentioned earlier, locale::locale(const locale&, const locale&,
  category) has been added without approval. It should be struck.

  22.1.1.3:

  Description of locale::use() Effects contains a nonsense statement:
  ``Because locale objects are immutable, subsequent calls to use<Facet>()
  return the same object, regardless of changes to the global locale.''
  If a locale object is immutable, then changes to the global locale
  should *always* shine through, for any facet that is not present
  in the *this locale object. If the intent is to mandate cacheing
  semantics, as sketched out in the original locales proposal, this
  sentence doesn't quite succeed. Nor should it. Cacheing of facets
  found in the global locale leads to horribly unpredictable behavior,
  is unnecessary, and subverts practically any attempt to restoee
  compatibility with past C++ practice and the current C Standard.
  The sentence should be struck.

  22.1.1.3:
  Description of locale::use Notes uses the term ``value semantics''
  and the verb ``to last.'' Are either of these terms defined within
  the Standard? The sentence should be reworded, or struck since it's
  non-normative anyway.

  22.1.1.5:
  locale::transparent() Notes says ``The effect of imbuing this locale
  into an iostreams component is unspecified.'' If this is a normative
  statement, it doesn't belong in a Notes clause. And if it's intended
  to be normative, it should be struck. Imbuing a stream with
  locale::transparent() is the *only* way to restore the behavior
  of iostreams to that in effect for *every* C++ programming running
  today. It is also essential in providing compatible behavior with
  the C Standard. The sentence should be struck.

  22.2.1.3.3:
  ctype<char> describes this subclause as ``overridden virtual functions,''
  but they're not. A template specialization has nothing to do with any
  virtuals declared in the template. Should be renamed.

  22.2.1.4:
  Description of codecvt, paragraph 3, fails to make clear how an
  implementation can ``provide instantiations for <char, wchar_t,
  mbstate_t> and <wchar_t, char, mbstate_t>.'' Must specializations
  be written for the template? If so, must they also have virtuals
  that do the actual work? Or can the implementation add to the
  default locale facets derived from the template, overriding the
  virtual do_convert? Needs to be clarified.

  22.2.1.4:
  Implementations should also be required to provide an instantiation
  of codecvt<char, char, mbstate_t> which transforms characters one
  for one (preferably by returning noconv). It is needed for
  the very common case, basic_filebuf<char>.

  22.2.1.4.2:
  codecvt::do_convert uses pointer triples (from, from_next, from_end)
  and (to, to_next, to_end) where only pairs are needed. Since the
  function ``always leaves the from_next and to_next pointers pointing
  one beyond the last character successfully converted,'' the function
  must be sure to copy from to from_next and to to to_next early on.
  A better interface would eliminate the from and to pointers.

  22.2.1.4.2:
  codecvt::do_convert says ``If no translation is needed (returns
  noconv), sets to_next equal to argument to.'' The previous paragraph
  strongly suggests that the function should also set from_next to
  from. Presumably, the program will call do_convert once, with nothing
  to convert. If it returns noconv, the program will omit future calls
  to do_convert. If that is the intended usage, then it should be
  permissible to call any instance of do_convert with (mostly)
  null pointers, to simplify such enquiries -- and the wording should
  make clear how to make such a test call.

  22.2.1.4.2:
  codecvt::do_convert Notes says that the function ``Does not write
  into *to_limit.'' Since there is no requirement that converted
  characters be written into sequentially increasing locations
  starting at to, this is largely toothless. Effects clause
  should be written more precisely.

  22.2.1.4.2:
  codecvt::do_convert Returns says that the function returns partial
  if it ``ran out of space in the destination.'' But the function
  mbrtowc, for example, can consume the remaining source characters,
  beyond the last delivered character, and absorb them into the
  state. It would be a pity to require do_convert to undo this
  work. Should say that partial can also mean the function ran out
  of source characters partway through a conversion. Then
  clarify that, after a return of partial, the
  next call to do_convert should begin with any characters between
  from_next and from_end, of which there might be none.

  22.2.2.1:
  Template class num_get defines the type ios as basic_ios<charT>,
  which it then uses widely to characterize parameters. Thus, this
  facet can be used *only* with iostreams classes that use the
  default traits ios_traits<charT>. Since the use of num_get is
  mandated for *all* basic_istream classes, this restriction rules out
  essentially any substitution of traits. Best fix is to make the
  ios parameter an ios_base parameter on all the do_get calls,
  then change ios accordingly. This is sufficient if
  setstate is moved to ios_base as proposed elsewhere. But it requires
  further fiddling for num_put if fill is moved *out* of ios_base,
  as also proposed. Must be fixed, one way or another.

  22.2.2.2:
  Template class num_put defines the type ios as basic_ios<charT>,
  which it then uses widely to characterize parameters. Thus, this
  facet can be used *only* with iostreams classes that use the
  default traits ios_traits<charT>. Since the use of num_put is
  mandated for *all* basic_ostream classes, this restriction rules out
  essentially any substitution of traits. Best fix is to make the
  ios parameter an ios_base parameter on all the do_put calls,
  then change ios accordingly. This is sufficient if
  setstate is moved to ios_base as proposed elsewhere. But it requires
  further fiddling for num_put if fill is moved *out* of ios_base,
  as also proposed. Must be fixed, one way or another.

  22.2.3.1:
  The syntax specified for numeric values is out of place in
  numpunct.

  22.2.3.1:
  Description of numpunct says, ``For parsing, if the digits portion
  contains no thousands-separators, no grouping constraint is applied.''
  This suggests that thousands-separators are permitted in an input
  sequence, and that the grouping constraint is applied, but it is
  profoundly unclear on how this might be done. Allowing thousands-
  separators at all in input is risky -- requiring that grouping
  constraints be checked is an outrageous burden on implementors,
  for a payoff of questionable utility and desirability. Should
  remove any requirement for recognizing thoudands-separators and
  grouping on input. And the effect on output needs considerable
  clarification.

  22.2.4:
  Template classes collate, time_get, time_put, money_get, money_put,
  money_punct, messages, and their support classes still have only
  sketchy semantics -- over a year after they were originally
  accepted into the draft. They are based on little or no prior
  art, and they present specification problems that can be addressed
  properly only with detailed descriptions, which do not seem to be
  forthcoming. Even if adequate wording were to magically appear
  on short notice, the public still deserves the courtesy of a
  proper review. For all these reasons, and more, the remainder
  of clause 22 from this point on should be struck.

  22.2.7.1:
  messages_base::THE_POSIX_CATALOG_IDENTIFIER_TYPE is not a defined type.

  27.1.1:
  The definition of ``character'' is inadequate. It should say that it
  is a type that doesn't need to be constructed or destroyed, and that
  a bitwise copy of it preserves its value and semantics. It should
  also say that it can't be any of the builtin types for which conflicting
  inserters are defined in ostream or extractors are defined in istream.

  27.1.2.4:
  Description of type POS_T contains many awkward phrases. Needs rewriting
  for clarity.

  27.1.2.4:
  Paragraph 2 has ``alg'' instead of ``all.''

  27.1.2.4:
  Footnote 207 should say ``for one of'' instead of ``for one if.''
  Also, it should``whose representation has at least'' instead of
  ``whose representation at least.''

  27.2:
  Forward declarations for template classes basic_ios, basic_istream,
  and basic_ostream should have two class parameters, not one. It
  is equally dicey to define ios, istream, etc. by writing just one
  parameter for the defining classes. All should have the second
  parameter supplied, which suggests the need for a forward reference
  to template class ios_char_traits as well, or at least the two usual
  specializations of that class.

  27.3:
  <iostream> is required to include <fstream>, but it contains no overt
  references to that header.

  27.3.1:
  cin.tie() returns &cout, not cout.

  27.3.2:
  win.tie() returns &cout, not cout.

  27.4:
  streamsize is shown as having type INT_T, but subclause 27.1.2.2
  says this is the integer form of a character (such as int/wint_t).
  streamsize really must be a synonym for int or long, to satisfy all
  constraints imposed on it. (See Footnote 211.)

  27.4:
  Synopsis of <ios> is missing streampos and wstreampos. (They appear
  in later detailed semantics.) Should be added.

  27.4:

  Synopsis of <ios> has the declaration:
   template <class charT> struct ios_traits<charT>;
  The trailing <charT> should be struck.

  27.4.1:
  Type wstreamoff seems to have no specific use. It should be struck.

  27.4.1:
  Type wstreampos seems to have no specific use. It should be struck.

  27.4.2:
  ios_traits::state_type is listed as ``to be specified.''
  It needs to be specified.

  27.4.2:
  Definition of ios_traits lists arguments backwards for is_whitespace.
  Should have const ctype<char_type>& second, as in later description.
  (Also, first argument should be int_type, as discussed in 27.4.2.3.)

  27.4.2:
  ios_traits description should make clear whether user specialization
  is permitted. If it isn't, then various operations in <locale> and
  string_char_traits are rather restrictive. If it is, then the draft
  should be clear that ios_traits<char> and ios_traits<wchar_t> cannot
  be displaced by a user definition.

  27.4.2:
  The draft now says ``an implementation shall provide'' instantiations
  of ios_traits<char> and ios_traits<wchar_t>. It was changed without
  approval from ``an implementation may provide.'' This change directly
  contradicts Nov 94 Resolution 23. The proper wording should be
  restored.

  27.4.2.2:
  ios_traits::not_eof should take an argument of int_type, not char_type.

  27.4.2.2:
  ios_traits::not_eof says nothing about the use made of its argument c.
  Should say that it returns c unless it can be mistaken for an eof().

  27.4.2.2:
  ios_traits::not_eof has two Returns clauses. The second is an
  overspecification and should be struck.

  27.4.2.2:
  ios_traits::length has an Effects clause but no Returns clause.
  The Effects clause should be reworded as a Returns clause.

  27.4.2.3:
  First argument to is_whitespace has been changed from int_type to
  char_type with no enabling resolution. It is also a bad idea.
  Should be restored to int_type.

  27.4.2.3:
  is_whitespace supposedly behaves ``as if it returns ctype.isspace(c),''
  but that function doesn't exist. Should say ``as if it returns
  ctype.is(ctype_base::space, c).''

  27.4.2.3:
  The draft now says that ios_traits functions to_char_type, to_int_type,
  and copy are ``provided from the base struct
  string_char_traits<CHAR-T>.'' This is a substantive change made
  without approval. It is also nonsensical, since there is no such
  ``base struct.'' The wording should be struck.

  27.4.2.4:
  ios_traits::to_char_type has an Effects clause which should be
  reworded as a Returns clause.

  27.4.2.4:
  ios_traits::to_int_type has an Effects clause which should be
  reworded as a Returns clause.

  27.4.2.4:
  ios_traits::copy has an Effects clause which should be
  reworded as a Returns clause. (It returns src.)

  27.4.2.4:
  ios_traits::get_state should be specified to do more than return zero.
  Semantics are inadequate. A pos_type conceptually has three
  components: an off_type (streamsize), an fpos_t, and a state_type
  (mbstate_t, which may be part of fpos_t). It must be possible
  to compose a pos_type from these elements, in various combinations,
  and to decompose them into their three parts.

  27.4.2.4:
  ios_traits::get_pos should be specified to do more than return
  pos_type(pos). Semantics are inadequate. See comments on get_state.
  above.

  27.4.3:
  ios_base::fill() cannot return int_type because it's not defined.
  Should be int if fill() is left in ios_base.

  27.4.3:
  ios_base::precision() and width() should deal in streamsize
  arguments and return values, not int. (Even more precisely,
  they should be moved to basic_ios and have all their types
  changed to traits::streamoff.)

  27.4.3.1.6:
  ~Init() should call flush() for wout, werr, and wlog, not just for
  cout, cerr, and clog.

  27.4.3.2:
  ios_base::fill(int_type) cannot receive or return int_type
  because it's not defined. Both should be int if fill(int_type)
  is left in ios_base.

  27.4.3.4:
  ios_base::iword allocates an array of long, not of int.

  27.4.3.4:
  ios_base::iword Notes describes a normative limitation on the lifetime
  of a returned reference. It should not be in a Notes clause. It should
  also say that the reference becomes invalid after a copyfmt, or when
  the ios_base object is destroyed.

  27.4.3.4:
  ios_base::pword Notes describes a normative limitation on the lifetime
  of a returned reference. It should not be in a Notes clause. It should
  also say that the reference becomes invalid after a copyfmt, or when
  the ios_base object is destroyed.

  27.4.3.5:
  Protected constructor ios_base::ios_base() must *not* assign initial
  values to its member objects as indicated in Table 72. That operation
  must be deferred until basic_ios::init is called. Should say here
  that it does no initialization, then move Table 72 to description of
  basic_ios::init (27.4.4.1). Also should emphasize that the object
  *must* be initialized before it is destroyed (thanks to reference
  counting of locale objects).

  27.4.3.5:
  Table 72 shows result of rdstate() for a newly constructed
  ios_base object, but that object defines no such member function.
  (Will be fixed if table is moved to basic_ios, as proposed.)

  27.4.4.1:
  basic_ios::basic_ios() has next to no semantics. Needs to be

  specified:
   Effects: Constructs an object of class basic_ios, leaving its
   member objects uninitialized. The object *must* be initialized
   by calling init(basic_streambuf *sb) before it is destroyed.

  27.4.4.1:
  basic_ios::init(basic_streambuf *sb) has no semantics.
  Needs to be specified:
   Postconditions: rdbuf() == sb, tie() == 0, ios_base initialized
   according to Table 72 (currently in 27.4.3.5).

  27.4.4.2:
  basic_ios::tie is not necessarily synchronized with an *input*
  sequence. Can also be used with an output sequence.

  27.4.4.2:
  basic_ios::imbue(const locale&) should call rdbuf()->pubimbue(loc)
  only if rdbuf() is not a null pointer. Even better, it should not
  call rdbuf()->pubimbue(loc) at all. Changing the locale that
  controls stream conversions is best decoupled from changing
  the locale that affects numeric formatting, etc. Anyone who knows
  how to imbue a proper pair of codecvt facets in a streambuf won't
  mind having to make an explicit call.

  27.4.4.2:
  basic_ios::imbue(const locale&) doesn't specify what value it returns.
  Should say it returns whatever ios_base::imbue(loc) returns.

  27.4.4.2:
  basic_ios::copyfmt should say that both rdbuf() and rdstate() are
  left unchanged, not just the latter.

  27.5.2:
  basic_streambuf::sgetn should return streamsize, not int_type

  27.5.2:
  basic_streambuf::sungetc should return int_type, not int

  27.5.2:
  basic_streambuf::sputc should return int_type, not int

  27.5.2:
  basic_streambuf::sputn should return streamsize, not int_type

  27.5.2.2.3:
  In in_avail Returns: gend() should be egptr() and gnext() should be
  gptr().

  27.5.2.2.3:
  basic_streambuf::sbumpc Returns should not say the function
  converts *gptr() to char_type. The function returns the int_type
  result of the call.

  27.5.2.2.3:
  basic_streambuf::sgetc Returns should not say the function
  converts *gptr() to char_type. The function returns the int_type
  result of the call.

  27.5.2.2.3:
  basic_streambuf::sgetn should return streamsize, not int.

  27.5.2.2.4:
  basic_streambuf::sungetc should return int_type, not int.

  27.5.2.2.4:
  basic_streambuf::sputc should return int_type, not int.

  27.5.2.2.5:
  basic_streambuf::sputc does not return *pptr(), which points at
  storage with undefined content. It returns traits::to_int_type(c).

  27.5.2.4.2:
  basic_streambuf::sync now requires that buffered input characters
  ``are restored to the input sequence.'' This is a change made without
  approval. It is also difficult, or even impossible, to do so for
  input streams on some systems, particularly for interactive or
  pipelined input. The Standard C equivalent of sync leaves
  input alone. Posix *discards* interactive input. This added requirement
  is none of the above. It should be struck.

  27.5.2.4.3:
  basic_streambuf::showmanyc Returns has been corrupted. The function
  should return the number of characters that can be read with no fear
  of an indefinite wait while underflow obtains more characters from the
  input sequence. traits::eof() is only part of the story. Needs to be
  restored to the approved intent. (See footnote 218.)

  27.5.2.4.3:
  basic_streambuf::showmanyc Notes says the function uses traits::eof().
  Not necessarily true.

  27.5.2.4.3:
  Footnote 217 is nonsense unless showmany is corrected to showmanyc.

  27.5.2.4.3:
  basic_streambuf::underflow has two Returns clauses. Should combine
  them to be comprehensive.

  27.5.2.4.3:
  basic_streambuf::uflow default behavior ``does'' gbump(1),
  not gbump(-1). It also returns the value of *gptr() *before*
  ``doing'' gbump.

  27.5.2.4.3:
  basic_streambuf::uflow has a nonsense Returns clause. Should be struck.

  27.5.2.4.4:
  basic_streambuf::pbackfail argument should be int_type, not int.

  27.5.2.4.4:
  basic_streambuf::pbackfail Notes begins a sentence with ``Other
  calls shall.'' Can't apply ``shall'' to user program behavior,
  by the accepted conformance model.

  27.6:
  <iomanip> synopsis has includes for <istream> and <ostream>, but
  none of the declarations appear to depend on either of these headers.
  They should be replaced by an include for <ios>.

  27.6:
  <iomanip> does *not* define a single type smanip. Rather, it
  defines at least two different types which depend on the type
  of the function argument. Should probably say that each function
  returns some unspecified type suitable for inserting into an
  arbitrary basic_ostream object or extracting from a basic_istream
  object.

  27.6.1.1:
  basic_istream::seekg(pos_type&) and basic_istream::seekg(off_type&,
  ios_base::seekdir) should both have const first parameters.

  27.6.11:
  basic_istream paragraph 2 says extractors may call rdbuf()->sbumpc(),
  rdbuf()->sgetc(), or ``other public members of istream except that
  they do not invoke any virtual members of rdbuf() except uflow().''
  This is a constraint that was never approved. Besides, rdbuf()->sgetc()
  invokes underflow(), as does uflow() itself, and the example
  of ipfx in 27.6.1.1.2 uses rdbuf()->sputbackc(). The added constraint
  should be struck.

  27.6.1.1:
  basic_istream definition, paragraph 4 is confusing, particularly in
  the light of similar errors in 27.6.2.1 and 27.6.2.4.2 (basic_ostream).
  It says, ``If one of these called functions throws an exception, then
  unless explicitly noted otherwise the input function calls
  setstate(badbit) and if badbit is on in exception() rethrows the
  exception without completing its actions.'' But the setstate(badbit)
  call may well throw an exception itself, as is repeatedly pointed
  out throughout the draft. In that case, it will not return control
  to the exception handler in the input function. So it is foolish to
  test whether badbit is set -- it can't possibly be. Besides, I can
  find no committee resolution that calls for exceptions() to be
  queried in this event.
  An alternate reading of this vague sentence implies that setstate
  should rethrow the exception, rather than throw ios_base::failure,
  as is its custom. But the interface to setstate provides no way
  to indicate that such a rethrow should occur, so these putative
  semantics cannot be implemented.
  The fix is to alter the ending of the sentence to read, ``and if
  setstate returns, the function rethrows the exception without
  completing its actions.'' (It is another matter to clarify what
  is meant by ``completing its actions.'')

  27.6.1.1.2:

  basic_istream::ipfx Notes says the second argument to traits::
  is_whitespace is ``const locale *''. The example that immediately
  follows makes clear that it should be ``const ctype<charT>&''.

  27.6.1.1.2:
  Footnote 222 makes an apparently normative statement in
  a non-normative context.

  27.6.1.2.1:
  basic_istream description is silent on how void* is converted.
  Can an implementation use num_get<charT>::get for one of the
  integer types? Must it *not* use this facet? Is a version of
  get missing in the facet? Needs to be clarified.

  27.6.1.2.1:
  Example of call to num_get<charT>::get has nonsense for first two
  arguments. Instead of ``(*this, 0, '' they should be
  be ``(istreambuf_iterator<charT>(rdbuf()),
  istreambuf_iterator<charT>(0), ''

  27.6.1.2.1:
  Example of numeric input conversion says ``the conversion occurs
  `as if' it performed the following code fragment.'' But that
  fragment contains the test ``(TYPE)tmp != tmp'' which often has
  undefined behavior for any value of tmp that might yield a true
  result. The test should be replaced by a metastatement such as
  ``<tmp can be safely converted to TYPE>''. (Then num_get needs
  a version of get for extracting type float to make it possible
  to write num_get in portable C++ code.)

  27.6.1.2.1:
  Paragraph 4, last sentence doesn't make sense. Perhaps ``since the
  flexibility it has been...'' should be, ``since for flexibility
  it has been...'' But I'm not certain. Subsequent sentences are
  even more mysterious.

  27.6.1.2.1:
  Use of num_get facets to extract numeric input leaves very unclear
  how streambuf exceptions are caught and properly reported. 22.2.2.1.2
  makes clear that the num_get::get virtuals call setstate, and hence
  can throw exceptions *that should not be caught* within any of the
  input functions. (Doing so typically causes the input function to
  call setstate(badbit), which is *not* called for as part of reporting
  eof or scan failure. On the other hand, the num_get::get virtuals
  are called with istreambuf_iterator arguments, whose very constructors
  might throw exceptions that need to be caught. And the description of
  the num_get::get virtuals is silent on the handling of streambuf
  exceptions.
  So it seems imperative that the input functions wrap each call to
  a num_get::get function in a try block, but doing so will intercept
  any exceptions thrown by setstate calls within the num_get::get
  functions.
  A related problem occurs when eofbit is on in exceptions and the
  program attempts to extract a short at the very end of the file.
  If num_get::get(..., long) calls setstate, the failure exception
  will be thrown before the long value is converted and stored in
  the short object, which is *not* the approved behavior.
  The best fix I can think of is to have the num_get::get
  functions return an ios_base::iostate mask which specifies what
  errors the caller should report to setstate. The ios& argument
  could be a copy of the actual ios for the stream, but with
  exceptions cleared. The num_get::get functions can then continue
  to call setstate directly with no fear of throwing an exception.
  But all this is getting very messy for such a time critical
  operation as numeric input.

  27.6.1.2.2:
  basic_istream::operator>>(char_type *) extracts an upper limit of
  numeric_limits<int>::max() ``characters.'' This is a silly and
  arbitrary number, just like its predecessor INT_MAX for
  characters of type char. A more sensible value is size_t(-1) /
  sizeof (char_type) - 1. Could just say ``the size of the largest
  array of char_type that can also store the terminating null.''
  basic_istream::operator>>(bool&) has nonsense for its first
  two arguments. Should be
    istreambuf_iterator<charT, traits>(rdbuf()),
    istreambuf_iterator<charT, traits>(0), etc.

  27.6.1.2.2:
  basic_istream::(bool&  paragraph 3 describes the behavior of
  num_get::get. Description belongs in clause 22.

  27.6.1.2.2:
  basic_istream::operator>>(unsigned short&) cannot properly check
  negated inputs. The C Standard is clear that -1 is a valid field,
  yielding 0xffff (for 16-bit shorts). It is equally clear that
  0xffffffff is invalid. But num_get::get(... unsigned long&)
  delivers the same bit pattern for both fields (for 32-bit
  longs), with no way to check the origin. One fix is to have
  the extractor for unsigned short (and possibly for unsigned int)
  pick off any '-' flag and do the checking and negating properly,
  but that precludes a user-supplied replacement for the num_get
  facet from doing some other magic. Either the checking rules
  must be weakened over those for Standard C, the interface to
  num_get must be broadened, or the extractor must be permitted
  to do its own negation.

  27.6.1.2.2:
  basic_istream::operator>>(basic_streambuf *sb) now says, ``If sb is
  null, calls setstate(badbit).'' This requirement was added without
  committee approval. It is also inconsistent with the widespread
  convention that badbit should report loss of integrity of the stream
  proper (not some other stream). A null sb should set failbit.

  27.6.1.2.2:
  basic_istream::operator>>(basic_streambuf<charT,traits>* sb), last
  line of Effects paragraph 4 can't happen. Previous sentence says,
  ``If the function inserts no characters, it calls setstate(failbit),
  which may throw ios_base failure. Then the last sentence says,
  ``If failure was due to catching an exception thrown while extracting
  characters from sb and failbit is on in exceptions(), then the caught
  exception is rethrown.'' But in this case, setstate has already thrown
  ios_base::failure. Besides, I can find no committee resolution
  that calls for exceptions() to be queried in this event.
  In fact, the approved behavior was simply to terminate the copy
  operation if an extractor throws an exception, just as for
  get(basic_streambuf&) in 27.6.1.3. Last sentence should be struck.

  27.6.1.3:
  basic_istream::get(basic_streambuf& sb) Effects says it inserts
  characters ``in the output sequence controlled by rdbuf().''
  Should be the sequence controlled by sb.

  27.6.1.3:
  basic_istream::readsome refers several times to in_avail(), which
  is not defined in the class. All references should be to
  rdbuf()->in_avail(). And the description should specify what
  happens when rdbuf() is a null pointer. (Presumably sets badbit.)

  27.6.1.3:
  basic_istream::readsome is now defined for rdbuf()->in_avail() < 0.
  The original proposal defined only the special value -1. Otherwise,
  it requires that rdbuf()->in_avail >= 0. Should be restored.

  27.6.1.3:
  basic_istream::readsome cannot return read, as stated. That
  function has the wrong return type. Should return gcount().

  27.6.1.3:
  basic_istream::putback does *not* call ``rdbuf->sputbackc(c)''.
  It calls ``rdbuf()->sputbackc(c)'' and then only if rdbuf()
  is not null.

  27.6.1.3:
  basic_istream::unget does *not* call ``rdbuf->sungetc(c)''.
  It calls ``rdbuf()->sungetc(c)'' and then only if rdbuf()
  is not null.

  27.6.1.3:
  basic_istream::sync describes what happens when rdbuf()->pubsync()
  returns traits::eof(), but that can't happen in general because
  pubsync returns an int, not an int_type. This is an unauthorized,
  and ill-advised, change from the original EOF. Return value should
  also be EOF.

  27.6.1.3:
  basic_istream::sync Notes says the function uses traits::eof().
  Obviously it doesn't, as described above. Clause should be struck.

  27.6.2.1:
  basic_ostream::seekp(pos_type&) and basic_ostream::seekp(off_type&,
  ios_base::seekdir) should both have const first parameters.

  27.6.2.1:
  basic_ostream definition, last line of paragraph 2 can't happen.
  It says, ``If the called
  function throws an exception, the output function calls
  setstate(badbit), which may throw ios_base::failure, and if badbit
  is on in exceptions() rethrows the exception.'' But in this case,
  setstate has already thrown ios_base::failure. Besides, I can find
  no committee resolution that calls for exceptions() to be queried
  in this event. Last sentence should end with, ``and if setstate
  returns, the function rethrows the exception.''

  27.6.1.2.1:
  Use of num_put facets to insert numeric output leaves very unclear
  how output failure is reported. Only the ostreambuf_iterator knows
  when such a failure occurs. If it throws an exception, the calling
  code in basic_ostreambuf is obliged to call setstate(badbit) and
  rethrow the exception, which is *not* the approved behavior
  for failure of a streambuf primitive.
  Possible fixes are: have ostreambuf_iterator report a specific
  type of exception, have ostreambuf_iterator remember a failure
  for later testing, or give up on restoring past behavior. Something
  *must* be done in this area, however.

  e7.6.2.4.1:
  Tabie 76 is mistitled. It is not just about floating-point conversions.

  27.6.2.4.1:
  Table 77 has an unauthorized change of rules for determining fill
  padding. It gives the three defined states of flags() & adjustfield as
  left, internal, and otherwise. It should be right, internal, and
  otherwise. Needs to be restored to the earlier (approved) logic.

  27.6.2.4.2:
  basic_ostream<<operator<<(bool) should use ostreambuf_iterator,
  not istreambuf_iterator. The first argument is also wrong in the
  call to num_put::put.

  27.6.2.4.2:
  basic_ostream::operator<<(basic_streambuf *sb) says nothing about
  sb being null, unlike the corresponding extractor (27.6.1.2.2).
  Should either leave both undefined or say both set failbit.

  27.6.2.4:
  basic_ostream::operator<<(streambuf *) says nothing about the failure
  indication when ``inserting in the output sequence fails''. Should
  say the function sets badbit.

  27.6.2.4.2:
  basic_ostream::operator<<(basic_streambuf<charT,traits>* sb), last
  line of Effects paragraph 2 can't happen. Previous sentence says that
  if ``an exception was thrown while extracting a character, it calls
  setstate(failbit) (which may throw ios_base::failure).'' Then the
  last sentence says, ``If an exception was thrown while extracting a
  character and failbit is on in exceptions() the caught exception
  is rethrown.'' But in this case, setstate has already thrown
  ios_base::failure. Besides, I can find no committee resolution
  that calls for exceptions() to be queried in this event. And an
  earlier sentence says unconditionally that the exception is rethrown.
  Last sentence should be struck.

  27.6.2.5:
  basic_ostream::flush can't test for a return of traits::eof() from
  basic_streambuf::pubsync. It tests for EOF.

  27.6.3:
  ``headir'' should be ``header.''

  27.6.3:
  <iomanip> does *not* define a single type smanip. Rather, it
  defines at least two different types which depend on the type
  of the function argument. Should probably say that each function
  returns some unspecified type suitable for inserting into an
  arbitrary basic_ostream object or extracting from a basic_istream
  object.

  27.7:
  <sstream> synopsis refers to the nonsense class int_charT_traits.
  It should be ios_traits.

  27.7:
  Table 77 (<cstdlib> synopsis) is out of place in the middle of
  the presentation of <sstream>.

  27.7.1:
  basic_stringbuf::basic_stringbuf(basic_string, openmode) Effects
  repeats the phrase ``initializing the base class with
  basic_streambuf().'' Strike the repetition.

  27.7.1:
  basic_stringbuf::basic_stringbuf(basic_string, openmode) Postconditions
  requires that str() == str. This is true only if which has in set.
  Condition should be restated.

  27.7.1:
  Table 78 describes calls to setg and setp with string
  arguments, for which no signature exists. Needs to be recast.

  27.7.1:
  basic_stringbuf::str(basic_string s) Postconditions
  requires that str() == s. This is true only if which had in set
  at construction time. Condition should be restated.

  27.7.1.2:
  Table 80 describes calls to setg and setp with string
  arguments, for which no signature exists. Needs to be recast.

  27.7.1.3:
  basic_stringbuf::underflow Returns should return int_type(*gptr()),
  not char_type(*gptr()).

  27.7.1.3:
  basic_stringbuf::pbackfail returns c (which is int_type) in first case,
  char_type(c) in second case. Both cases should be c.

  27.7.1.3:
  basic_stringbuf::pbackfail supposedly returns c when c == eof().
  Should return traits::not_eof(c).

  27.7.1.3:
  basic_stringbuf::seekpos paragraph 4 has ``positionedif'' run together.

  27.8.1.1:
  basic_filebuf paragraph 3 talks about a file being ``open for reading
  or for update,'' and later ``open for writing or for update.''
  But ``open for update'' is not a defined term. Should be struck
  in both cases.

  27.8.1.3:
  basic_filebuf::is_open allegedly tests whether ``the associated file
  is available and open.'' No definition exists for ``available.''
  The term should be struck.

  27.8.1.3:
  basic_filebuf::open Effects says the function fails if is_open()
  is initially false. Should be if initially true.

  27.8.1.3:
  basic_filebuf::open Effects says the function calls the default
  constructor for basic_streambuf. This is nonsense. Should say,
  at most, that it initializes the basic_filebuf as needed, and
  then only after it succeeds in opening the file.

  27.8.1.3:
  Table 83 has a duplicate entry for file open mode ``in | out''.

  27.8.1.4:
  filebuf::showmanyc (and several overriden virtual functions that
  follow) have a Requires clause that says ``is_open == true.''
  The behavior of all these functions should be well defined
  in that event, however. Typically, the functions all fail.
  The Requires clause should be struck in all cases.

  27.8.1.4:
  filebuf::showmanyc Effects says the function ``behaves the same
  as basic_streambuf::showmanyc.'' The description adds nothing
  and should be struck.

  27.8.1.4:
  basic_filebuf::underflow effects shows arguments to convert as
  ``st,from_buf,from_buf+FSIZE,from_end,to_buf, to_buf+to_size, to_end''.
  st should be declared as an object of type state_type, and n should
  be defined as the number of characters read into from_buf. Then the
  arguments should be ``st, from_buf, from_buf + n, from_end, to_buf,
  to_buf + TSIZE, to_end''. Also, template parameter should be
  ``traits::state_type,'' not ``ios_traits::state_type.''

  27.8.1.4:
  basic_filebuf::underflow is defined unequivocally as the function
  that calls codecvt, but there are performance advantages to having
  this conversion actually performed in uflow. If the specification
  cannot be broadened sufficiently to allow either function to do
  the translation, then uflow loses its last rationale for being
  added in the first place. Either the extra latitude should be
  granted implementors or uflow should be removed from basic_streambuf
  and all its derivatives.

  27.8.1.4:
  basic_filebuf::pbackfail(traits::eof()) used to return a value
  other than eof() if the function succeeded in backing up the
  input. Now the relevant Returns clause says the function returns
  the metacharacter c, which is indistinguishable from a failure
  return. This is an unapproved change. Should probably say the
  function returns traits::not_eof(c).

  27.8.1.4:
  basic_filebuf::pbackfail Notes now says ``if is_open() is false,
  the function always fails.'' This is an unapproved change.
  The older wording should be restored.

  27.8.1.4:
  basic_filebuf::pbackfail Notes now says ``the function does not
  put back a character directly to the input sequence.'' This is
  an unapproved change and not in keeping with widespread practice.
  The older wording should be restored.

  27.8.1.4:
  basic_filebuf::pbackfail has a Default behavior clause.
  Should be struck.

  27.8.1.4:
  basic_filebuf::overflow effects shows arguments to convert as
  ``st,b(),p(),end,buf,buf+BSIZE,ebuf''. st should be declared as
  an object of type state_type. Then the arguments should be
  ``st, b, p, end, buf, buf + BSIZE, ebuf''. Also, template parameter
  should be ``traits::state_type,'' not ``ios_traits::state_type.''

  27.8.1.4:
  basic_filebuf::overflow doesn't say what it returns on success.
  Should say it returns c.

  27.8.1.4:
  basic_filebuf::setbuf has no semantics. Needs to be supplied.

  27.8.1.4:
  basic_filebuf::seekoff Effects is an interesting exercise in creative
  writing. It should simply state that if the stream is opened as a
  text file or has state-dependent conversions, the only permissible
  seeks are with zero offset relative to the beginning or current
  position of the file. (How to determine that predicate is another
  matter -- should state for codecvt that even a request to convert
  zero characters will return noconv.) Otherwise, behavior is largely
  the same as for basic_stringstream, from whence the words should be
  cribbed. The problem of saving the stream state in a traits::pos_type
  object remains unsolved. The primitives described for ios_traits
  are inadequate.

  27.8.1.4:
  basic_filebuf::seekpos has no semantics. Needs to be supplied.

  27.8.1.4:
  basic_filebuf::sync has no semantics. Needs to be supplied.

  27.8.1.4:
  basic_filebuf::imbue has silly semantics. Whether or not sync()
  succeeds has little bearing on whether you can safely change
  the working codecvt facet. The most sensible thing is to establish
  this facet at construction. (Then pubimbue and imbue can be
  scrubbed completely.) Next best is while is_open() is false.
  (Then imbue can be scrubbed, since it has nothing to do.)
  Next best is to permit any imbue that doesn't change the facet
  or is at beginning of file. Next best is to permit change of facet
  any time provided either the current or new facet does not mandate
  state-dependent conversions. (See comments under seekoff.)

  27.8.1.7:
  basic_filebuf::rdbuf should not have explicit qualifier.

  27.8.1.9:
  basic_ofstream::basic_ofstream(const char *s, openmode mode = out)
  has wrong default second argument. It should be `out | trunc', the
  same as for basic_ofstream::open (in the definition at least).

  27.8.1.10:
  basic_ofstream::open(const char *s, openmode mode = out)
  has wrong default second argument. It should be `out | trunc', the
  same as for basic_ofstream::open in the definition.

  27.8.2:
  <cstdio> synopsis has two copies of tmpfile and vprintf,
  no vfprintf or putchar.

  27.8.2:
  <cwchar> summary should also list the type wchar_t. Aside from the
  addition of the (incomplete) type struct tm, this table 84 is
  identical to table 44 in 21.2. It is not clear what purpose either
  table serves; it is less clear what purpose is served by repeating
  the table.

  27.8.2:
  See Also reference for <wchar> should be 7.13.2, not 4.6.2.

  D.2:
  Functions overloaded on io_state, open_mode, and seek_dir ``call
  the corresponding member function.'' But no hint is given as to
  what constitutes ``correspondence.''

  D.3.1.3:
  strstreambuf::overflow has numerous references to ``eof()'', which
  no longer exists. All should be changed to EOF.

  D.3.1.3:
  strstreambuf::overflow says it returns ``(char)c'' sometimes,
  but this can pun with EOF if char has a signed representation.
  More accurate to say it returns (unsigned char)c.

  D.3.1.3:
  strstreambuf::pbackfail says it returns ``(char)c'' sometimes,
  but this can pun with EOF if char has a signed representation.
  More accurate to say it returns (unsigned char)c.

  D.3.1.3:
  strstreambuf::pbackfail says it returns ``(char)c'' when c == EOF,
  but this can pun with EOF if char has a signed representation.
  More accurate to say it returns something other than EOF.

  D.3.1.3:
  strstreambuf::pbackfail twice says it returns EOF to indicate
  failure. Once is enough.

  D.3.1.3:
  strstreambuf::setbuf has a Default behavior clause, which is not
  appropriate for a derived stream buffer. It also adds nothing to
  the definition in the base class. The entire description should
  be struck.

  =========================================================================

  Public Review Comment #T22

  Library

  ___________________________________________________________________________
  To: X3SEC
  From: sehari@iastate.edu on Wed, Jul 5, 1995 4:26 PM
  Subject: Suggestions for C++ Language
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;5 Jul 1995 16:23:06
  -0500
  Received: by eng3.iastate.edu with sendmail-5.65
  	id <AA11920@eng3.iastate.edu>; Wed, 5 Jul 1995 15:26:30 -0500
  Date: Wed, 5 Jul 1995 15:26:30 -0500
  From: sehari@iastate.edu
  Message-Id: <9507052026.AA11920@eng3.iastate.edu>
  To: x3sec@itic.nw.dc.us
  Subject: Suggestions for C++ Language

  From:  	Babak Sehari
         	37A Schilletter Vil.
         	Ames, Iowa 50014-1418
         	(515) 296 8555
         	sehari@iastate.edu

  To: 	X3J16 Committee
      	1250 Eye St. Suite 200
      	Washington, DC 20005
      	(202) 626 5738
      	x3sec@itec.nw.dc.us

  Subject:  Suggestion for input and output of C++ for international usage.

  						
  Date:  July 3rd, 1995.

  Dear Committee members,

  	In order to make C++ programming language more international, the
  terminal input and output functions of C++ should be able to handle various
  languages requirement.	Due to the fact that some languages such as Chinese
  are
  written from top to bottom and some other languages such as Arabic, Handi,
  Urdu,
  and Persian are written from right to left, a C++ Standard should be
  able to deal with input and output in these languages using all terminal
  functions.
  This can be done using a call to overload the functions and operators,
  such as:

  char term_dir( char direction);

  where direction may be defined as:

  		0   left to right ( normal English)
  		1   right to left
  		2   top to bottom
  return value:

  		0 unsuccessful
  		1 successful

  This call will effect behavior of functions such as printf and scanf and
  overloads
  operators such as << and >>.

  For example to read and write a chinese document after English text
  one can write:

  char load_chinese_fonts();  // a function to be defined by the programmer

  main()
  {

         char answer[10],answer2[10];

         printf("The language would you prefer?)"
         scanf(answer);
         printf("\n");
         load_chinese fonts();
         term_dir(2);
         printf("chinese text");
         scanf(answer2);	 // now the scanf should enter the data from top to
  bottom

  }

  Thank you, for your considerations.

  		  With highest regards,
  		       Babak E.  Sehari.


  =========================================================================

  Public Review Comment T23

  Core
  _____________________________________________________________________________
  To: X3SEC
  From: WEIDMAN on Wed, Jul 5, 1995 11:54 AM
  Subject: C++ Standards
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;5 Jul 1995 11:42:06
  -0500
  Received: by sealex.kaman.com (5.65/fma-120691);
  	id AA24049; Wed, 5 Jul 1995 11:43:29 -0400
  Received: from MR.KAMAN.COM by BOB.KAMAN.COM (PMDF V5.0-3 #5041)
   id <01HSIDCVJFG0000D5R@BOB.KAMAN.COM> for x3sec@itic.nw.dc.us; Wed,
   05 Jul 1995 11:47:02 -0400 (EDT)
  Received: with PMDF-MR; Wed, 05 Jul 1995 11:46:37 -0400 (EDT)
  Mr-Received: by mta KSCALX.MUAS; Relayed; Wed, 05 Jul 1995 11:46:37 -0400
  Mr-Received: by mta KSCALX; Relayed; Wed, 05 Jul 1995 11:46:38 -0400
  Mr-Received: by mta BOB; Relayed; Wed, 05 Jul 1995 11:45:10 -0400
  Disclose-Recipients: prohibited
  Date: Wed, 05 Jul 1995 11:46:37 -0400 (EDT)
  From: WEIDMAN <weidman-alx1@kaman.com>
  Subject: C++ Standards
  To: x3sec@itic.nw.dc.us
  Message-Id: <6937461105071995/A35361/KSCALX/11972AEE2400*@MHS.KAMAN.COM>
  Autoforwarded: false
  Mime-Version: 1.0
  Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  Content-Transfer-Encoding: 7BIT
  Importance: normal
  Priority: normal
  Sensitivity: Company-Confidential
  Ua-Content-Id: 11972AEE2400
  X400-Mts-Identifier: [;6937461105071995/A35361/KSCALX]
  Hop-Count: 2

  Dear Ms. Barra:

  I am a longtime C programmer who recently learned C++.
  I have several concerns after learning the language.

  Point 1:
  Imagine:

  class cA { ... };
  class cB : public cA {...};

  main()
  	{
  	cB *pB = new cB [10];
  	cA *pA = (cA *)pB;

  	}

  If cA and cB are different sizes, then there is no
  convenient way of accessing other than the first
  element of pA.  It would have been nice to say
  pA[1] and gotten the same address as pB[1], but
  this is not the case.  I understand that this would
  imply checking the virtual table for each of the
  elements in the pA array to determine their sizes,
  kind of destroying the default definition of [], but
  if I define cA::operator[](int);, then I need to
  access this using either

  (*pA)[1];
  or
  pA[0][1];

  neither of which is particularly convenient.

  Point 2:
  	The language is really unbelievably
  cluttered.  As long as C++ remains a superset
  of C, this will be true.  It seems, however, that
  once one has defined the concept of "Reference",
  then one can pretty much do away with the
  concept of "Pointer," since both are stored as
  pointers within the object code.  This would take
  C++ well away from C, but it would make the language
  significantly less cluttered.

  Point 3:
  	I have run across some compilers that
  give "Anachronism" warnings when encountering
  delete [n] pX;
  These compilers seem to feel that
  delete pX;
  should be sufficient, although they all fail to call
  cX::~cX() more than once if this so-called "Anachronism"
  is eliminated.  A clear standard on whether the
  delete [n] structure is needed, and whether multiple
  destructors should be called would be quite helpful.

  I realize that some of my ideas really stretch the
  language beyond its present state, but perhaps
  throwing out the seeds of these ideas could help
  the language grow in some future, if not this,
  standards version.

  Thank you for your attention,
  Greg Weidman
  Software Engineer
  Kaman Sciences Corp.
  2560 Huntington Ave.
  Alexandria, Va. 22303
  (703)-329-4331
  weidman-alx1@kaman.com


  =========================================================================

  Public Review Comment T24

  Library

  _____________________________________________________________________________
  Received: from cs.rpi.edu by vnet.IBM.COM (IBM VM SMTP V2R3) with TCP;
     Thu, 06 Jul 95 14:15:12 EDT
  Received: from procyon.cs.rpi.edu by cs.rpi.edu (5.67a/1.4-RPI-CS-Dept)
  	id AA23164; Thu, 6 Jul 1995 14:15:16 -0400 (root from procyon.cs.rpi.edu)
  Date: Thu, 6 Jul 95 14:01:49 EDT
  From: musser@cs.rpi.edu
  Received: by procyon.cs.rpi.edu (4.1/2.3-RPI-CS-client)
  	id AA24130; Thu, 6 Jul 95 14:01:49 EDT
  Message-Id: <9507061801.AA24130@procyon.cs.rpi.edu>
  To: x3sec@itic.nw.dc.us
  Cc: josee@VNET.IBM.COM
  Subject: Adding hashed containers to the C++ standard library

  As a participant in the design of the Standard Template Library
  and the first implementor of associative containers, I've received
  many queries about why hashed containers are not included in the C++
  standard library.  As a public comment on the X3J16 standard, I would
  like to propose that hashed containers now be added. The motivations
  for having hashed containers and a detailed proposal for how to add
  them, with minimal change to the current specification of associative
  containers (23.1.2), can be found in

   J. Barreiro, R. Fraley, and D. Musser, "Hash Tables for the
   Standard Template Library," available by anonymous ftp from
   ftp.cs.rpi.edu as public/stl/hashdoc.ps.Z.

  A more detailed rationale can be found in

   D. Musser, "A Rationale for Adding Hash Tables to the Standard
   Template Library," available by anonymous ftp from
   ftp.cs.rpi.edu as public/stl/hashrationale.ps.Z.

  I am also sending postscript files for these papers in the next two
  messages.

  Please let me know if you need any additional information
  at this time.


  David R. Musser
  Rensselaer Polytechnic Institute	(518) 276-8660
  Computer Science Department		musser@cs.rpi.edu
  Troy, NY 12180				FAX: (518) 276-4033

  =========================================================================

  Public Review Comment T25

  Core

  _____________________________________________________________________________
To: uunet!x3sec@itic.nw.dc.us
Cc: plauger!plum@uunet.uu.net; uunet!dmitry@cup.hp.com
From: P.J. Plauger on Thu, Jul 6, 1995 12:46 PM
Subject: Additional WG14 comments on C++ draft
RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;6 Jul 1995 12:46:05
-0500
Received: from uucp5.UU.NET by relay3.UU.NET with SMTP
	id QQyxfv16671; Thu, 6 Jul 1995 12:49:30 -0400
Received: from plauger.UUCP by uucp5.UU.NET with UUCP/RMAIL
        ; Thu, 6 Jul 1995 12:49:30 -0400
Date: Thu, 6 Jul 95 12:21:30 EST
From: plauger!pjp@uunet.uu.net (P.J. Plauger)
Subject: Additional WG14 comments on C++ draft
Message-ID: <9507061221.0.UUL1.3#20134@plauger.UUCP>
To: uunet!x3sec@itic.nw.dc.us
CC: plauger!plum@uunet.uu.net, uunet!dmitry@cup.hp.com

Date: 6 July 1995

From: ISO/IEC JTC1/SC22/WG14 (programming language C)

To:   ISO/IEC JTC1/SC22/WG21 (programming language C++)

Subject: Additional comments on C++ draft presented for CD balloting

INTRODUCTION

The following comments were not included in the 5 July 1995
Review document sent from WG14 to WG21. Please append them
to that document.

------------------------------

 Comments on C++ draft, dated 1995-04-28.

 Terminology: A.B-C means subclause A.B, paragraph C.

 3.2-8

 The acronym ``ODR'' has not been defined.  Also, it doesn't
 make sense when expanded: ``one definition rule rule''.

 3.7.3.2-5

 Footnote #20 refers to ``architectures''.  Other places
 refer to ``machines''.  They should all refer to
 ``implementations''.

 3.8

 It is not clear what object ``use'' or ``reuse'' is.

 3.8-2

 The acronym ``POD'' has not been defined.  In general, each
 section should have ``forward references'', like the C
 Standard.

 3.8-3

 Awkward wording: ``In particular, except as noted''.

 3.9-2

 How can I tell that the ``copy operation is well-defined''?
 It is not clear what ``well-defined'' means here or if I can
 test for it.

 3.9-4 The ``value'' of an object of type T is not
 necessarily based upon its bit represention, especially when
 the class is a handle to other data.  The ``value'' in this
 case would depend upon how the "==" operator is overloaded.
 Even if its ``representation value'' is somehow defined,
 what purpose does it serve?  Where else is this used in the
 draft?

 3.9.1-1

 Remove ``there are several fundamental types''.

 3.9.1-2

 Use different wording than ``take on''.

 3.9.1-4

 Don't refer to ``machine architecture''.  See C Standard
 wording.

 3.9.1-6

 Change ``laws of arithmetic modulo 2^N'' to C Standard
 wording.

 3.9.1-8

 Reword ``although values of type bool generally behave
 ...''.

 3.9.1-8

 Reword ``successfully be stored''.

 3.9.2-1

 Reword ``There is a conceptually infinite ...''.  Remove the
 words ``conceptually'' and ``infinite''.

 3.9.3-1

 The definition of "volatile" is missing.  It isn't in
 subclause 1.8 or 7.1.5.1.  See the C definition: ``An object
 that has volatile-qualified type may be modified in ways
 unknown to the implementation ...''.

 3.9.3-5

 Change ``In this document'' to ``In this International
 Standard''.

 3.10-2

 Footnote #30: Clarify ``... in some sense refer to an
 object''.

 4.1-1

 Reword ``necessitates ... is ill-formed'' to use ``shall''
 or ``shall not''.

 4.1-1

 Footnote #31.  Need proper reference to Standard C.

 4.3-1

 Footnote #32.  Reword ``there is no way ...''.

 4.5-1

 Reword ``can'' with ``shall'.

 4.4-4

 The sentence ``That is, the member aspect ...'' should be a
 footnote.

 4.5-2

 Reword ``can'' with ``shall'.

 4.5-3

 Reword ``can'' with ``shall'.

 4.5-3

 Footnote #34: Reword ``If the bit-field is larger yet, ...''
 using ``shall'' and ``shall not''.  If this is a constraint,
 it shouldn't be a footnote.

 4.5-4

 Reword ``can'' with ``shall'.

 4.7-2

 What is the difference here between a note and a footnote?
 This should be a footnote.

 5.2.2-7

 A bit-field is not a type.

 5.2.2-7

 Change ``unsigned'' to ``unsigned int'' ``int, unsigned int,
 ...''.

 5.2.6-1

 Reword ``... shall not cast away constness'' in more precise
 terms.  See 5.2.9-2's reference to 5.2.10.

 5.2.7-1

 Footnote #43: Does "*(p)" meet this requirement?

 5.2.7-1

 Shouldn't ``then the pointer shall either be zero'' be
 ``then the pointer shall either be the null pointer value''?

 5.2.8-1

 Reword ``... shall not cast away constness'' in more precise
 terms.  See 5.2.9-2's reference to 5.2.10.

 5.2.10

 This section is hard to understand, especially the rules
 defining casting away constness.

 5.2.10-4

 Does ``implicit conversion'' here refer to subclause 4.10,
 pointer conversion?

 5.2.10-7

 The ``[Note:'' doesn't have a closing ``]''.  This appears
 to be a formatting issue throughout the document.

 5.2.10-7

 Where are ``multi-level' and ``mixed object'' defined?

 5.3.1-2

 How do the ``implicit conversions'' here relate to the
 ``implicit conversions'' of 4.10 or 5.2.10?  The term
 ``implicit conversion'' should be defined explicitly.

 5.3.5-2

 What is ``(_class.conv,fct_)''?

 5.7-6

 How is C compatibility maintained if a different header is
 required for C++ for "ptrdiff_t"?

 5.8-1

 Why isn't the C wording used here, especially the semantics
 for unsigned integers?

 5.9-2

 Change ``The usual arithmetic conversions'' to ``The
 standard integral promotions (4.5)''.

 5.10-1

 It is not clear ``.... have the same semantic restrictions,
 conversions'' what this points to.  The wording should be
 repeated or the reference to the associated text should be
 clearer.

 5.11-1

 See 5.9-2 above on ``usual arithmetic conversions''.

 5.12-1

 See 5.9-2 above on ``usual arithmetic conversions''.

 5.13-1

 See 5.9-2 above on ``usual arithmetic conversions''.

 5.16-1

 What was the grammar changed from C?  The expression after
 the colon should be ``conditional-expression''.

 5.16-2

 If both the second and third expression are throw-
 expressions, then what it the type of the result?  According
 to 15-1, the resultant type of the throw expression is
 "void".  Thus, the resultant type of "?:" is "void".  This
 should be made clear here.

 5.17-4

 Change ``the user'' to ``the program''.  Change all other
 uses of ``the user'' to something else in the rest of the
 draft.

 7.1.2-2

 Change ``hint'' wording to use C wording similar to
 "register" keyword.

 7.1.3-5

 Reword ``... The typedef-name is still only a synonym for
 the dummy name and shall not be used where a true class name
 is required''.  Either ``dummy name'' should be defined or
 removed in this paragraph (used several times).  What is a
 ``true class name''?  If the dummy name is not specified,
 why do I care about it for ``linkage purposes''?

 7.1.5.1-3

 The draft says ``CV-qualifiers are supported by the type
 system so that they cannot be subverted without casting'',
 but it doesn't specify that the behavior is undefined (C
 says it's undefined).

 7.1.5.1-7

 This should not be a note, but part of the standard.  The
 same wording should be extracted from the C Standard.

 7.2-1

 Reword or remove ``... not gratuitously larger than int''.
 If it's implementation-defined, then say so.

 7.2-6

 The possibility that the compiler generates bit fields for
 enumerators means that it would not be object, i.e., not
 addressible.  Since it is impossible to determine whether or
 not the address is taken (the "enum" might have its address
 taken in some other translation unit), having the compiler
 decide bit-field or not won't work.  If "enum" bit fields
 are to be supported, they should use some *obvious* syntax.
 Also, implicit bit fields would be incompatible with C
 programs.

 7.3.1.2-1

 If an unnamed namespace has a unique identifier that cannot
 be determined and cannot be linked to (even if there is
 external linkage -- see footnote 54), then an unnamed
 namespace is equivalent to "static" at file scope.  The
 draft should change the wording to be the equivalent of
 "static" at file scope (a feature all linkers can provide)
 rather than the requirement that a unique name be created
 (difficult for linkers and *very* difficult for externally
 developed libraries).  If an implementation creates
 something that I cannot detect then it doesn't exist.

 7.3.3-6

 Remove ``... (as ever)''.

 7.3.4-4

 What is a ``using-directive lattice''?  Where is it defined?

 7.5-3

 If a function has more than one linkage specification (say
 in different translation units) a diagnostic is required.
 However, the compiler and/or linker may not be able to
 detect this even with type-safe linking (type-safe linking
 doesn't imply that the function call mechanisms are the
 same).

 7.5-6

 Reword ``There is no way ...''.

 7.5-8

 Change: ``FORTRAN'' is now properly spelled ``Fortran''
 according to the Fortran Standard.  It would be better if
 C++ specified that the linkage string is case insensitive
 and is in the ISO 646 subset.  Since the linkage is all
 implementation-defined anyway, the linker (and the compiler)
 will know the true way (possibly, case-sensitive) of
 spelling the linkage name.

 8-3

 Footnote 55: Reword ``A declaration with several
 declarations is usually equivalent'' to remove the word
 ``usually''.

 8.2-1

 Reword ``In that context, it surfaces ...'' to remove
 ``surfaces''.

 8.2-1

 Remove ``Just as for statements''.  The reference to which
 section is unclear.  The level of semantics to drag in are
 not specified.

 8.2-2

 Reword ``... can occur in many different contexts ...'' to
 remove the word ``many''.

 8.2-3

 Number the 4 examples.

 8.3-2

 Change ``inductive'' to ``recursive''.

 8.3.1-3

 Reword ``volatile specifiers are handled similarly.''.
 Similar to what?

 8.3.2-4

 Reword ``In particular, null references are prohibited; no
 diagnostic is required.''.  What does ``prohibited'' mean?
 Do you mean ``undefined'' here?

 8.3.4-1

 Typo: ``,T'' ==> ``T,'', ``.T'' ==> ``T.''

 8.3.4-2

 Replace with C Standard wording.  The C wording is clearer
 and shorter: ``An array type describes a contiguously
 allocated nonempty set of objects with a particular member
 object type, called the element type.'' (there is a footnote
 attached that explains imcomplete types are disallowed).

 8.3.4-3

 Reword ``When several array of specifications are adjacent''
 to remove or define the word ``adjacent''.

 8.3.4-4

 Reword ``... (say, N) ...'' to remove the ``say, N''.
 Possibly, start the sentence ``If N is the number of initial
 elements, ...''.

 8.3.5-2

 Is using the C "<cstdarg>" the same as "<stdarg.h>"?  If
 not, then the code will be incompatible.

 8.3.5-4

 Remove ``Functions shall not return arrays ... [to the end
 of the paragraph]''.  This restriction has been stated
 elsewhere.

 8.3.5-5

 Remove this paragraph.  It has been stated elsewhere.

 8.3.6-6

 Change ``out-of-line function'' to ``non-inline function''.

 8.3.6-9

 Previously, the order of evaluation of function arguments
 was ``unspecified''.  Here it's ``implementation-defined''.
 Which is it?

 8.5-6

 Need forward reference to ``POD''.  It has not yet been
 defined.  The restriction on arrays has been stated
 elsewhere.

 18.1-3

 If the C standard header "<stddef.h>" is used, do I get the
 same result as including "<cstddef>"?  Subclause D.1 refers
 to compatibility, but this isn't clear.  Also, this
 paragraph should refer to D.1.

 D.1-1

 The names should be the same for C headers in C++.  There
 should be no renaming.  This breaks C code to rename them,
 especially when both should behave the same.  Rather than
 the name "cstdlib", is should be "stdlib.h".  Since every C
 compiler already supports this, C++ can't claim defective
 linkers, filesystems, and so on.  This is a gratuitous
 difference that just breaks working code.


  =========================================================================

  Public Review Comment T26

  T26.1 Core
  T26.2 LIbrary

  _____________________________________________________________________________
  To: X3SEC
  Cc: bkline@cortex.nlm.nih.gov
  From: bob_kline@stream.com on Thu, Jul 6, 1995 1:27 PM
  Subject: re: X3 Announces the Public Comment Period of ... C++
  RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;6 Jul 1995 13:26:14
  -0500
  Received: by hawk.canton.csof.com; id NAA13559; Thu, 6 Jul 1995 13:23:33
  -0400
  Received: from smtpmail.canton.csof.com(199.170.32.248) by
  hawk.canton.csof.com via smap (V1.3)
	id sma013518; Thu Jul  6 13:23:08 1995
  Received: from cc:Mail by smtpmail.canton.csof.com
	id AA805062946; Thu, 06 Jul 95 12:54:53 EST
  Date: Thu, 06 Jul 95 12:54:53 EST
  From: "bob kline" <bob_kline@stream.com>
  Message-Id: <9506068050.AA805062946@smtpmail.canton.csof.com>
  To: x3sec@itic.nw.dc.us
  Cc: bkline@cortex.nlm.nih.gov
  Return-Receipt-To: bob_kline@stream.com
  Subject: re: X3 Announces the Public Comment Period of ... C++

  The following comments are submitted on the recently released draft
  standard for C++.  Hard copy will be mailed to the appropriate
  addresses.

  ----------------------------------------------------------------------
--------------------------------

  T26.1 Core

--------------------------------

2.9.2 [lex.ccon]: A change has been made to octal escape sequences,
which until now has always been a backslash followed by one, two, or
three octal digits.  The latest version appears to place no limit on the
number of digits which can make up an octal escape sequence.  The ANSI C
standard introduced a similar change for hex escape sequences, which
broke quite a bit of existing code (which relied on the assumption that
the translator would stop folding characters into the escape sequence
after two hex digits had been seen).  So, for example, "\x1eFourier
Series" has a different interpretation with an ANSI C compiler than it
did with an older compiler.  When the problem was created by the new
standard there were (at least) two solutions available.  One solution
was to take advantage of the new requirement that adjacent string
literals be concatenated by the preprocessor; thus the example could be
re-written as "\x1e" "Fourier Series", preventing the F of Fourier from
being swallowed into the escape sequence.  The second solution was to
use octal escape sequences, which were guaranteed to contain no more
than 3 octal digits.  This solution would write the example as
"\036Fourier Series".  Of the two solutions, the second (octal) solution
offers the advantage that the result was correctly interpreted by all C
compilers (including older compilers which don't handle the
concatenation of adjacent strings).  It was therefore disappointing to
learn that code which used this solution to the problem introduced by
ANSI C would now be broken.  It was also puzzling, since the change does
not allow the programmer to do anything which could not be accomplished
with the ANSI C scheme.  Since one of the published goals is to break as
little existing code as possible, and only do so when there is no
alternate means of introducing a significant new needed feature to the
language, I assume this change is inadvertant and will be corrected
before the standard is approved and published.  The change would be even
more drastic than that introduced by ANSI C, because it would break code
which relies on an assumption which has been valid from the earliest
versions of C, whereas the hex escape sequences were not part of K&R C,
and there was therefore less existing code to break.  Please restore the
original definition of octal escape sequences.

----------------------------------------------------------------------

5.3.1 [expr.unary.op]: The sentence following the third example
("Neither does qualified-id, ....") is outside the square brackets
enclosing the example,  but continues the thought begun within the
brackets.  Text containing bracketed portions should read intelligibly
if the bracketed material is omitted.

----------------------------------------------------------------------

5.3.5 [expr.delete]: Footnote 46: "... deleted using a point ...."
Should read "... deleted using a pointer ..."

----------------------------------------------------------------------

7.1.5.1 [dcl.type.cv] Paragraph 2: "... for a const object of type T, if
T is a class with a user-declared default constructor, the constructor
for T is called, ...."  This language implies that for the following
code fragment

    class T {
    public:
        T();
        T(int);
        ....
    };
    const T t(1);

the default constructor would be called for t.  Surely this is not what
the committee intended.

----------------------------------------------------------------------

7.1.5.1 [dcl.type.cv]: In paragraph 6 the semicolon is missing after
definition of class Y.

----------------------------------------------------------------------

8.5.1 [dcl.init.aggr]: Two pointers to footnote 62 appear: one in
paragraph 1 and the other in paragraph 4.  Only the one in paragraph 4
seems appropriate.  Is there a footnote missing for paragraph 1?

----------------------------------------------------------------------

9.3 [class.scope0]: Paragraph 1, rule 2: use the same font for S in both
places.

----------------------------------------------------------------------

10.3 [class.virtual]: Paragraph 4: "Even if destructors are not
inherited, a destructor in a derived class overrides a base class
destructor declared virtual; ...."  This should read "Even though
destructors ...."

----------------------------------------------------------------------

12.4 [class.dtor]: Paragraph 10 gives examples of placement of an object
of class X at a buffer created as

    static char buf[sizeof(X)];

Is the alignment of a static array of char guaranteed to satisfy the
alignment requirements of an arbitrary class X?

----------------------------------------------------------------------

12.7 [class.cdtor]: Example in paragraph 2: why is `D((C*)this,'
commented out?

----------------------------------------------------------------------
--------------------------------

  T26.2 Library

--------------------------------


21.1.1.4 [lib.string.cons]: Why do some constructor specifications
indicate what is thrown under exceptional conditions and others not?
Also, for basic_string(const chrT*), shouldn't length_error be thrown if
 n >= npos (draft says `if n == npos')?  Also, signatures given in the
tables do not always match the prototypes for the corresponding
constructors; (e.g.: table 42: basic_string(size_type, charT, ...) vs.
(charT, size_type); and table 43 uses identifiers instead of the type
names).  Also, under table 43, "Notes: see Table ___, ...": the table
reference is incomplete.  As a general comment, some of the library
chapters appear to have received much less thorough editorial scrutiny
than the chapters for the language proper.

----------------------------------------------------------------------

21.1.1.6 [lib.string.capacity]: "size_type max_size() const;  Returns:
The maximum size of the string."  This description does not convey
enough information.  Does this mean the maximum value that can be given
to resize()?  Does it reflect space for a terminating NUL?  Does it
reflect the amount of space currently allocated?  (If so, how would this
differ from capacity()?)

----------------------------------------------------------------------

21.1.1.10.1:
 template<class charT, class traits, class Allocator>
   basic_string<charT,traits,Allocator>
     operator+(const basic_string<charT,traits,Allocator>& lhs,
               const basic_string<charT,traits,Allocator>& rhs);
 Returns lhs.append(rhs).

If you look back as 21.1.1.8.2, basic_string::append, you see that
basic_string::append() is a non-const member function, which means that
it can't be used to implement operator+(), for which lhs is a const
object.  It wouldn't make sense anyway, because that would duplicate the
functionality of basic_string::operator+= (see 21.1.1.8.1).  Don't we
want operator+ to create an entirely new object, not just append to lhs?

----------------------------------------------------------------------

27.1.1 [lib.iostreams.definitions]: Paragraph 1, last entry: "A
repositional stream, can seek to only the position where we previously
encountered.  On the other hand, an arbitrary-positional stream can
seek to any position within the length of the stream.  Every
arbitrary-positional stream is repositional."
 - The comma after "repositional stream" needs to be deleted.
 - The third sentence contradicts the first as worded.
 - The colloquial and awkward tone ("where we previously encountered")
   is inconsistent with the more impersonal and precise language of
   the rest of the standard.

----------------------------------------------------------------------

27.1.2 [lib.iostreams.type.reqmts]: Last sentence: "... expects to the
character container class." should read "... expects of the character
container class."

----------------------------------------------------------------------

27.1.2.1 [lib.iostreams.char.t]: "provides the definitions common
between ..." should read "provides the definitions common to ...."

----------------------------------------------------------------------

27.1.2.3 [lib.iostreams.off.t]: footnote 207: "It is usually a synonym
for one of the signed basic integral types whose representation at least
as many bits as type long."  Should read "... whose representation is at
least as many bits as type long."

----------------------------------------------------------------------

27.1.2.3 [lib.iostreams.off.t]: Paragraph 4: "[Type OFF_T is c]onvertible
to type POS_T.  But no validity of the resulting POS_T value is ensured,
whether or not the OFF_T value is valid."  Of what use is the conversion,
then?

----------------------------------------------------------------------

27.1.2.4 [lib.iostreams.pos.t]: Paragraph 3's sentence is awkwardly
worded ("... previous position previously obtained") and needs to be
completed.

----------------------------------------------------------------------

27.1.2.4 [lib.iostreams.pos.t]: table 66: first row has assertion
"p == P(i)" but p does not appear in the expression for that row; also,
that row has the note "a destructor is assumed" -- what does this mean?

----------------------------------------------------------------------

27.4.2.2 [lib.ios.traits.values]:
 "int_type not_eof(char_type c);
  Returns: a value other than the end-of-file, even if c == eof().
  Notes: It is used in basic_streambuf<charT,traits>::overflow().
  Returns: int_type(c) if c!=eof()."

Why are the two "Returns:" sections separated? The description of
basic_streambuf<charT,traits>::overflow() sheds no light on the use of
this function.  Can we have a less oblique explanation?

----------------------------------------------------------------------

27.4.2.4 [lib.ios.traits.convert]:
 "state_type get_state(pos_type pos);
  Returns: 0."

Can we get an explanation?

----------------------------------------------------------------------

27.4.3.2 [lib.fmtflags.state]:
 "int width() const;
  Returns: The field width (number of characters) to generate on certain
    output conversions."

Should read "Returns: The minimum field width ...."

----------------------------------------------------------------------

27.4.3.4 [lib.ios.base.storage]:
 "long& iword(int idx);
  Effects: If iarray is a null pointer, allocates an array of int ...."

Why not an array of long?  Also, "Notes: After a subsequent call to
iword(int) for the same object, the earlier return value may no longer
be valid."  This note (and the footnote accompanying it) appear to imply
that it would be impossible to rely on the use of this function to store
a value in the array, then come back to read it with a second call to
the function.

----------------------------------------------------------------------

27.4.3.5 [lib.ios.base.cons]: In table 72, "rdstate() [returns] goodbit
if sb is not a null pointer, otherwise badbit."  Where is `sb'
explained?  Also, the fonts in this table need to be used consistently.

----------------------------------------------------------------------

27.5.1 [lib.streambuf.reqts]: Paragraph 3, 3rd constraint: "If xnext
is not a null pointer and xbeg < xnext for an input sequence, then a
putback position is available.  In this case, xnext[-1] shall have a
defined value and is the next (preceding) element to store a character
that is put back into the input sequence."  The wording of the last
sentence is fuzzy.

----------------------------------------------------------------------

27.5.2.3.1 [lib.streambuf.get.area]:
 "char_type* egptr() const;
  Returns: The end pointer for the output sequence."

Should be "... pointer for the input sequence."

----------------------------------------------------------------------

27.5.2.4.1 [lib.streambuf.virt.locales]: "Between invocations of this
function a class derived from streambuf can safely cache results of calls
to locale functions and to members of facets so obtained."  Does this mean
that changes in locale can be effectively ignored by the streambuf?

----------------------------------------------------------------------

27.6 [lib.iostream.format]: under "Header <iomanip> synopsis: `typedef
? smanip;' -- What does this mean?

----------------------------------------------------------------------

27.6.1.2 Formatted input: What has happened to the input operators for
unsigned char?

----------------------------------------------------------------------

27.6.1.1.2 [lib.istream.prefix]: in paragraph 1: "Otherwise it calls
setstate(failbit) (which may throw ios_base::failure (27.4.4.3)) and
returns false."

How about "... and (if an exception is not thrown) returns false."

----------------------------------------------------------------------

27.6.1.2.1 [lib.istream.formatted.reqmts]: Paragraph 3 seems to imply
that if extraction of a floating-point value from a stream encounters
a value which has more precision than can be held in a float, and
operator>>(float&) is used, the fail bit will be set.  Will this not
be an unexpected outcome for most programmers?

----------------------------------------------------------------------

27.6.1.2.1 [lib.istream.formatted.reqmts]: Paragraph 5: "In case the
converting result is a value of either an integral type ... or a float
type ... performing to parse and convert the result depend on the
imbued locale object."  This is really French converted to English
by translation software, right? :->}

----------------------------------------------------------------------

27.6.1.2.2 [lib.istream::extractors]: Paragraph 2: "If the function stores
no characters, it calls setstate(failbit), which may throw ios_base::failure
(27.4.4.3).  In any case, it then stores a null character ...."  How can
it store anything if an exception is thrown?  C++ does not use the
resumption model for exception handling.  Different language than "In
any case" is needed here.

----------------------------------------------------------------------

27.6.1.2.2 [lib.istream::extractors]: Paragraph 2:
 "basic_istream<charT,traits>& operator>>(char_type& c);
  Effects: Extracts a character, if one is available, and stores it in c.
    Otherwise, the function calls setstate(failbit)."

Not eofbit?

----------------------------------------------------------------------

27.6.1.2.2 [lib.istream::extractors]: Paragraph 3:
 "basic_istream<charT,traits>& operator>>(short& n);
  Effects: Converts a signed short integer, if one is available, and
    stores it in n."

Why does the document identify what happens when a character is not
available (see paragraph 2), but not when a number is not available?

----------------------------------------------------------------------

27.6.1.4 [lib.istream.manip]: "... saves a copy of is.fmtflags ...."
Should this not read "... saves a copy of is.flags ...."?

----------------------------------------------------------------------

27.6.2.4.2 [lib.ostream.inserters]:
 "basic_ostream<charT,traits>& operator<<(unsigned long n);
  Effects: Converts the unsigned long integer n with the integral
    convertsion specified preceded by l."

Should this be "... preceded by ul."?

----------------------------------------------------------------------

27.7 [lib.string.streams]: table 77 ("Header <cstdlib> synopsis") appears
to be out of place.  Furthermore, the top row of the table: "Type ...
Name(s)" doesn't seem to match the data in the table, which only
contains names, but no types.

----------------------------------------------------------------------

27.8.1 [lib.fstreams], paragraph 2: "... the type name FILE is a synonym
for the type FILE."  This seems like an odd sort of synonym, doesn't it?
Also, the last sentence of this subsection, "Because of necessity of
the conversion between the external source/sink streams and wide
character sequences." is incomplete.

----------------------------------------------------------------------

27.8.1.3 [lib.filebuf.members]:
 "bool is_open() const;
  Returns: true if the associated file is available and open.

  basic_filebuf<charT, traits>* open(const char * s, ios_base::openmode
mode);
  Effects: If is_open() == false, returns a null pointer.  Otherwise, calls
    basic_streambuf<charT,traits>::basic_streambuf() (27.5.2.1).  It then
    opens a file, if possible, whose name is the NTBS s ("as if" by
    calling ::fopen(s,modstr))."

Why does open() only open the file if is_open() is not already true?
At best, the sequencing is confused here.

----------------------------------------------------------------------

27.8.1.4 [lib.filebuf.virtuals]: No description is given for
setbuf(char_type *, int).  Also, descriptions for seekpos(), sync(),
and imbue() are also missing or hopelessly jumbled (e.g., the
description of imbue(const locale& loc) talks only about calling sync()).

----------------------------------------------------------------------

General comment: Initialisms (POD, for example), should be expanded at
the location of their first occurrence, or (better) placed in a
glossary, or (best) both.

----------------------------------------------------------------------

This is probably too late to make it into the standard (unless the
process rolls into further extensive revisions and balloting anyway,
which -- judging from the state of the Input/Output library section --
seems likely :->}), but I'll point it out it all the same.  If we really
want programs to use the iostreams package instead of the FILE I/O
calls, the iostreams package should provide as a minimum the same
facilities as the older library.  Specifically, the standard C I/O
package provides a convenient method for controlling the maximum number
of characters to write in formatted I/O, e.g.:

   fprintf(fp, "FONT NAME: %.16s\n", font_desc.font_name);

This handles the case of a structure which has enough space for
a string which will not necessarily be NUL-terminated if the
maximum number of characters are stored for the string (a common
enough situation when one is manipulating data structures written
by someone else's software).

What are the reasons for leaving this out of the iostreams package?
Also (while on the topic of rounding out iostreams to match what
the competition can do), how difficult would it be to provide the
ability to control the (minimum) number of digits in the exponent
for a formatted floating point number written using scientific
notation (as, for example, one can do in Ada)?

----------------------------------------------------------------------

Thanks very much for the opportunity to submit comments on the draft.
I'm sure some of them reflect no more than deficiencies in my
understanding of how the language is supposed to behave.  I hope the
rest will be helpful.

Bob Kline


  =========================================================================

  Public Review Comment T27

  Extensions

_______________________________________________________________________________
To: X3SEC
From: Herb Sutter on Mon, Jul 10, 1995 3:18 PM
Subject: Revised: X3J16 Public comment, proposed current_class keyword
RFC Header:Received: by gateway.itic.nw.dc.us with SMTP;10 Jul 1995 15:06:56
-0500
Received: from herbs.interlog.com (herbs.interlog.com [199.212.152.143]) by
gold.interlog.com (8.6.10/8.6.10) with SMTP id PAA01251 for
<x3sec@itic.nw.dc.us>; Mon, 10 Jul 1995 15:09:56 -0400
Message-Id: <199507101909.PAA01251@gold.interlog.com>
X-Sender: herbs@mail.interlog.com
X-Mailer: Windows Eudora Version 1.4.3
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Mon, 10 Jul 1995 15:14:21 -0400
To: x3sec@itic.nw.dc.us
From: herbs@interlog.com (Herb Sutter)
Subject: Revised: X3J16 Public comment, proposed current_class keyword

This is a revised version of what I emailed on Friday, containing
corrections and amplifications.  I'm also putting two signed copies in the
mail to the appropriate places as requested.

Please feel free to contact me by email and/or include me in the email
discussions; I'd appreciate the opportunity to discuss this proposal
further, and Tokyo may be a little far for me to travel in November, so
email would be best.

Thanks again,

Herb



===================================================================

                      Draft Standard Comment:

               The Case For a current_class Keyword



       Submitted by: Herb Sutter, Connected Object Solutions

                    email: herbs@interlog.com

                       Friday, July 7, 1995

        2228 Urwin, Suite 102, Oakville ON Canada, L6L 2T2
            Tel.: (416) 618-0184   Fax: (905) 847-6019
                    email: herbs@interlog.com

===================================================================


-------------------------------------------------------------------
ABSTRACT


Allowing  base classes to know the exact type of the current  most-
derived class in which they are being used is a great advantage  at
low cost, because it is both:

   a) essential for writing entire classes of mixin designs,
      particularly generic patterns; and

   b) implementable with one keyword without impacting the existing
      language rules.

I  will  describe  the  motivation  for  a  proposed  current_class
keyword,  whose  equivalent functionality already exists  in  other
languages  (e.g., Eiffel's like current, Sather's  SAME).   I  will
then demonstrate current_class's usefulness by implementing several
mixin/pattern designs from current literature (e.g., Gamma et al.'s
Design Patterns), showing, through failed workaround attempts, that
the   same   results  cannot  be  obtained  without   current_class
information.

To address the Design and Evolution s6.4.1 questions in a nutshell:
The  proposed  change  will be shown to be widely  applicable  (the
entire industry is getting into patterns and generic patterns)  and
general-purpose  (applicable  for other  mixins,  see  Example  4),
essential  for generic pattern and other mixin programming  styles,
specifically   beneficial  for  the  design/implementation/use   of
libraries, and free of side effects in the existing standard.   The
proposed  change does not affect the efficiency of code  that  does
not  use it, acts as a specialised compiler-supported template (and
adds   a   vtable)  to  classes  which  do  use  it,  requires   no
recompilation  of existing programs, and does not  affect  external
linkage.   The change is a single keyword which has already  proven
easy  to  grasp in Usenet discussions, and is unlikely to  lead  to
demands  for  further  extensions because  it  is  a  simple  self-
contained concept.



-------------------------------------------------------------------
SUGGESTED FEATURE: DESCRIPTION


To implement transparent pattern mixins, we need a mechanism for  a
base class to know the type of (though not being able to declare  a
member variable of) the current derived class.  I propose that this
take the form of a current_class keyword.  For example, consider:

  class A {
       current_class* Function() { return
  dynamic_cast<current_class*>(this); };
  };

  class B : public A {...};   // a B's Function() returns a B*
  class C : public B {...};   // a C's Function() returns a C*

This   example  illustrates  three  main  advantages  that  I  will
illustrate further:

   a) current_class allows generic reuse of a function (e.g.,
      A::Function()) in derived classes without requiring redundant
      redefinitions to supply correct current types;

   b) together with the existing dynamic_cast, it allows a base class
      to know and use the true this pointer for the current object
      (via dynamic_cast<current_class*>(this));

   c) it also allows further polymorphic dispatch of a base member
      function's return value (e.g., the result of Function()).

The  next  four examples will illustrate that this is not  possible
with  current  methods, despite attempts at workarounds  using  the
"class D : public B<D>" idiom, which fails in each case.  What will
be   illustrated  is  that,  in  essence,  using  current_class  is
implicitly  parameterising on a type (in this case, that  parameter
is  always filled in with the current class), and therefore current
template  rules  continue to work properly  and  as  expected  when
applied to classes invoking current_class.



-------------------------------------------------------------------
EXAMPLE 1: IMPLEMENTING THE SINGLETON PATTERN


The  first example considers implementing a generic version  of  an
example  from  Design  Patterns: the Singleton  pattern.   It  will
illustrate   the  key  points,  including  current   (and   failed)
workaround attempts.  (Because this pattern is so simple, some have
questioned  why  one would put such a simple design  into  its  own
generic pattern; but this example is for illustration and the  same
issues arise with patterns supplying more complex behaviour.)

Consider  designing  a  class MySingle  that  should  behave  as  a
singleton; that is, we want it to have one (or a controlled set of)
instance(s)  with  a global point of reference.   To  implement  it
today,  we  supply the proper private static _instance pointer  and
public static Instance() function manually:

      class MySingle {
      public:
          static MySingle* Instance() {
              if (!_instance)
                  _instance = new MySingle;
              return _instance;
          };
      private:
          static MySingle* _instance;

      // ...plus MySingle-specific attributes and behaviour, for example:
      //
      public:
          MyMethod (...);
      }

      MySingle* MySingle::_instance = 0;

However,  this  has a serious drawback: We have  to  respecify  and
reimplement  the  pattern member variable and  function  for  every
class we want to have behave as a Singleton, which prevents generic
reuse.   Instead, we would like to generalise the Singleton pattern
into  a  class  or  template  that we  can  just  inherit  from  or
instantiate, like:

      class MySingle : public Singleton {   // if only we could do this!
      public:
          MyMethod (...);
      };  // complete definition, equivalent in every way to the above

The problem is that we can't write such a generic Singleton pattern
under  the  draft standard.  A naive attempt would be to  duplicate
the core Singleton in its own class (identically to the above):

    class Singleton {
    public:
        static Singleton* Instance() {
            if (!_instance)
                _instance = new Singleton;
            return _instance;
        };
    private:
         static  Singleton* _instance;    // omitting for now the issue
     };                                   // of how to even manage this

    class MySingle : public Singleton {
    public:
        MyMethod (...);
    };  // not quite good enough

The main problem is that this solution is not equivalent to writing
our  singleton class manually, for now applications can  no  longer
call:

     MySingle::Instance()->MyMethod();   // error

as  they  should because MySingle::Instance() returns a Singleton*,
not  a  MySingle*.  This solution requires the application  to  use
RTTI on the returned pointer:

     (dynamic_cast<MySingle*>MySingle::Instance())->MyMethod()

which is tedious and error-prone.  Or, alternatively we could foist
off   the   work   onto   the   MySingle   programmer   by   making
Singleton::Instance() pure virtual, forcing (and relying on) him to
override it in MySingle and all its derivatives; yet even then  the
function  will  be identical in every class except for  its  return
type  and  it  is  wasteful (and dangerous) to  try  to  force  the
programmer  to repeat it even with a macro.  And, even  if  it  was
workable,   it  would  still  not  address  the  issue  of   proper
transparent reuse.

The  next obvious question is, What if we used templates (note: see
"Questions  &  Answers" section later on for comments  on  attempts
using function templates)?

    template <class T> class Singleton {
    public:
        static T* Instance() {
            if (!_instance)
                _instance = new T;
            return _instance;
        };
    private:
        static T* _instance;
    };

    template<class T>
    T* Singleton<T>::_instance = 0;

Can  we  derive  what we want?  Yes, as long as we  always  inherit
directly from the mixin:

    class MySingle : public Singleton<MySingle> {
    public:
        MyMethod (...);
    };  // better, but must derive from Singleton<> directly

This works properly as long as we don't try to inherit further from
MySingle,  in which case it fails miserably because further-derived
classes  will  have  as  their  ancestor  Singleton<MySingle>,  not
Singleton<WhatIReallyAm>.  In general, consider:

  template<class T> class Pattern { /*...uses actual type T...*/ };

  class FirstClass : public Pattern<FirstClass> { /*...so far so good...*/ };

  class OtherClass : public FirstClass { /*...oops...*/ };

With  a  Pattern implemented as above to imitate the  current_class
information,  only  immediately derived  classes  behave  properly.
OtherClass now is derived from a Pattern instantiated with MyClass,
which  is wrong (note this problem would not appear if Pattern  had
real  current_class information).  To fix this, you need to  resort
to a parallel class hierarchy like:

    Pattern        FirstClassProto
     |  \         /      |
     |   \       /       |
     |  FirstClass  OtherClassProto
      \            /
       \          /
        OtherClass

The fatal problem here is not that this workaround doubles the size
of  the  class hierarchy or even that it's error-prone;  the  fatal
problem  is  that it destroys polymorphism between  FirstClass  and
OtherClass  and so forces a very inelegant tradeoff: We may  either
have  polymorphism or mix in generic patterns, but we  may  not  do
both.    This  restriction  seems  unreasonable,  especially   when
current_class solves the whole problem simply and elegantly:

    class Singleton {
    public:
        static current_class* Instance() {
            if (!_instance)
                _instance = new current_class;
            return _instance;
        };
    private:
        static current_class* _instance;
    };

    current_class* Singleton::_instance = 0;   // see below

Now we can go ahead and cleanly mix in our generic pattern:

    class MySingle : public Singleton {
    public:
        MyMethod (...);
    };  // ...and we're done, cleanly and elegantly.

Now,  getting  back to that static variable: Can we  deal  with  it
easily?   Yes,  simply by remembering that the use of current_class
can be viewed as an implicit type parameterisation; in other words,
as  a  specially supported implicit template.  Since  clearly  each
class  should have its own static _instance member (since the  type
will  differ),  each class should indeed get  its  own  copy.   For
example:

  class B { public: static current_class* _p; /*...*/ };
  current_class* B:_p = 0;   /* generates B* B::_p = 0; */

  class D1 : public B { /*...*/ };
  /* transparently generates D1* D1::_p = 0; as would a template */

  class D2 : public D1 { /*...*/ };
  /* transparently generates D2* D2::_p = 0; as would a template */



-------------------------------------------------------------------
EXAMPLE 2: MULTIPLE DISPATCH, VARYING RETURN TYPES


Here is an actual recent Usenet question that can be satisfied only
with  current_class  information, and it  illustrates  another  key
advantage:

In article <DB9CtB.6Er@eunet.ch>,
   xar@pax.eunet.ch (Benjamin Rosenbaum) wrote [with corrections]:
>Wouldn't it be nice if function overloading were
>polymorphic by arguments, so that in...
>
>  class A { public: void do_f() { f(*this); }; };
>  class B: public A {};
>
>  void f(A&) { cout << "this gets called..."; };
>  void f(B&) { cout << "...but wouldn't this be better?"; };
>
>  main () {
>    B b;
>    b.do_f();
>  }

Again  the template workaround could be applied, but again for  the
same  reasons deriving more than one level deep would  fail  (i.e.,
the  workaround  would  work for this example,  but  fails  in  the
general  case,  for  example if we had  a  class  C  :  public  B).
However, consider:

  class A {
  public:
      void do_f()  { f(dynamic_cast<current_class*>(this)); }
  };

Now  this  program  works as desired, with the  rest  of  the  code
unchanged.   This  demonstrates  that  the  current_class  facility
allows  not  only  easy multiple dispatch, but  in  general  allows
proper  polymorphism  on  the return  type  of  a  mixed-in  member
function, which is a very powerful tool in many situations.



-------------------------------------------------------------------
EXAMPLE 3: IMPLEMENTING THE VISITOR PATTERN


In the same article came the following example:

  >I want to implement the Visitor pattern from Gamma et.al.'s Design
  >Patterns, as shown below (it's for double-dispatch-in-C++.)  Without
  >the macro, you have to make sure to type the same damn thing in every
  >subclass of A, since the only thing we want to vary is the scope of
  >"this"!  Is there any way to do it with templates?  Even better, is
  >there a way to really make sure it gets done in every subclass of A?
  >(I guess it could be pure virtual in A).

  <example using macro to redefine accept() member function in
   every subclass elided>

Using the template workaround and the parallel-hierarchy structure,
we  get  (the  following  is a modified  version  of  the  poster's
original code):

  class Visitor {
  public:
    void visit (A&) { cout << "Visited A" << endl; };
    void visit (B&) { cout << "Visited B" << endl; };
  };

  template<class T> class Visitee {
  public:
    virtual void accept (Visitor &v) {
  v.visit(dynamic_cast<T&>(*this)); };
  };

  class A_impl {};
  class B_impl : public A_impl {};  // keep polymorphism in ?_impl classes
                                    // for whatever that's worth
  class A : public A_impl, public Visitee<A> {};
  class B : public B_impl, public Visitee {};

  main() {
   A a;
   B b;
   Visitor v;
   a.accept(v);  // calls v.visit(A&)
   b.accept(v);  // calls v.visit(B&)
  }

Again,  the problem is that to get mixins we are forced to give  up
polymorphism (here between A and B).  But with current_class we can
define a proper Visitee mixin that works properly in all cases:

  class Visitee {
  public:
    virtual void accept (Visitor &v) {
      v.visit(dynamic_cast<current_class&>(*this));
    };
  };
  class A: public Visitee {};
  class B: public A {};  // inherits the mixin naturally, can't be done today
                      // note, no dual class hierarchy, simple/elegant design

This  single  change  eliminates the  need  for  the  double  class
hierarchy,  simplifies the design, and allows not only  polymorphic
multiple  dispatch  but  also the writing  of  an  easily  reusable
generic pattern.



-------------------------------------------------------------------
EXAMPLE 4: A ProtectedObject MIXIN


Consider  a  mixin  class, called ProtectedObject,  which  supplies
behaviour  for storing a checksum of an object's physical  bits  so
that  the  object  can  detect  unauthorised  modifications  (e.g.,
through rogue pointers elsewhere in the program, or through cosmic-
ray  strikes in software controlling a comm satellite,  etc.).   To
protect  an object, we would like to simply include ProtectedObject
in its class' inheritance list to mix in the behaviour; then all we
should need to do is call the generic SetCheck function at the  end
of  each  of our own functions that can change the internal  state,
and  call the generic Check() function at the start of each of  our
own functions to ensure no tampering has taken place.

This  cannot  be  done in a generic base class without  letting  it
somehow:  a)  get the correct this pointer for the current  object;
and  b)  determine  the  size  of the object.   With  current_class
information, we can create such a generic mixin:

  class ProtectedObject {
  protected:
      bool Check()    { return (_check == CalcCheck()); };
      void SetCheck() { _check = CalcCheck(); };
  private:
      int  CalcCheck()
      { /* calculate and return a checksum based on bytes in memory from
           dynamic_cast<current_class*>(this) up to
           dynamic_cast<current_class*>(this) + sizeof(current_class) - 1,
           correcting for the checksum's own value as needed */
      };
      int  _check;
  };

This kind of mixin is not possible under the current draft for  the
same  reasons  as in the other examples: Derived class  information
cannot  be  brought  into the base class without  using  a  special
template   as   a   workaround,  which  workaround  itself   breaks
polymorphism (among other drawbacks).



-------------------------------------------------------------------
QUESTIONS & ANSWERS

...................................................................
Q1 (from Usenet):
>I guess this is inspired by Eiffel's "like current" feature?

A1:   No,  though I guess this means I'm not the first to see  this
need.    Others   mentioned  the  same  thing,  and  that   similar
functionality exists in Sather.  Clearly, then, this seems to be  a
useful construct already present in other OO languages.

...................................................................
Q2 (from Usenet):
>Should the following be type-correct?
>
>    class A {
>        current_class* Function() { return new A; };
>    };

A2:   No,  for  the  new  keyword doesn't  change  existing  rules.
Specifically,  since  there's no implicit conversion  from  a  base
pointer  to a derived pointer, this would only be right when  A  is
concrete  and  is  itself the current class  (therefore  the  above
definition, while legal, would simply prevent derivation  from  A).
Again,    remember   that   using   current_class   is   implicitly
parameterising  on  a type, and therefore the template  and  typing
rules still work fine.

...................................................................
Q3:   Can  I declare a member variable of type current_class?   For
example:

  class Pattern {
  private:
      current_class myVariable;  // can cause size problems if allowed
  };

A3:  No.  This should be  illegal, because:  a) it would  mean  the
base class portion would actually change size in each derived class,
wreaking havoc with existing rules; and  b) if a class does need to
create/use  an instance of current_class, it  can already do so (by
storing a  pointer to it  and allocating it on  the heap,  as  does
Singleton in Example 1 above).  In short, base classes may use  and
store pointers or references to objects of type current_class,  but
may not store member variables of the type itself.

...................................................................
Q4: Function Template Workarounds
    (from Usenet, commenting on Example 1 above)
>>now applications can no longer call:
>>     MySingle::Instance()->MyMethod();   // error
>
>Do it via a regular function template instead:
>
>    template<class C> inline
>    C* instance<C>() { return static_cast<C*>(C::Instance()); }
>
>so your above call becomes:
>    instance<MySingle>()->MyMethod();

A4:   This has several problems.  From least to greatest, they are:
a) it seems inelegant because it removes pattern functions from the
pattern;  b)  it  is  error-prone since  it  relies  on  programmer
discipline  in  the client code; c) it does not solve  the  problem
since  Singleton  still needs derived class information  for  other
reasons  (i.e., to declare its _instance pointer with  the  correct
type).  To elaborate on (a), every function requiring current_class
information  must be moved out of the pattern class  and  templated
separately.

It  also  does  not solve the other Examples: e.g., we  cannot  get
Example 2's mainline, but would end up with something like:

  template<class C>  do_f(C c) { f(c); };
  main() {
      B b;
      do_f(b);  // essentially doing manual polymorphism instead of
b.do_f()
  }                // also, allows do_f<A>(b) even when slicing is
undesirable

For  the  commented reasons, this isn't equivalent to the  proposed
current_class solution.

...................................................................
Q5: Resolving Casts  (from Usenet, commenting on Example 2 above)
>>
>>  class A {
>>  public:
>>      void do_f()  { f(dynamic_cast<current_class*>(this)); }
>>  };
>
>And how must a compiler/linker resolve that call?
>I think that for this to work, we would have to add a significant
>burden to compiler writers.

A5:   No.   If  we treat current_class as an implicit parameterised
type (template) that is specially supported to be always filled  in
with  the  current type (which the compiler already knows about  in
all  the  cases where it can be used), the existing template  rules
work properly.

...................................................................
Q6: Translation Units  (from Usenet, commenting on Example 2 above)
>
>Consider: (your example with definition of do_f moved to some other file).
>    class X { public: void do_f(); };
>
>    // In some other file:
>    void X::do_f() { f(dynamic_cast<current_class*>(this)); }
>
>Now, how would you implement this?

A6:   Recall:  Using  current_class is an implicitly  parameterised
type  (template) always filled with the current most-derived class.
Now,  when  you  write a template that uses a parameterised  class'
member  function (e.g., writing template<class T> and  later  using
T::operator++)  it  must  be  visible to  all  instantiated/derived
classes;  i.e.,  it must be in the header, and not in  a  different
translation   unit.    Similarly,   any   member   function   using
current_class  (and possibly the entire base class) must  be  fully
defined in the header.

<several alternatives demonstrated to be unworkable elided>
> Option c) generate new X::do_f() functions for each derived class.

Exactly, since using current_class is to use an implicit template.

> This would require a new virtual function table entry for
> do_f, otherwise the dynamic_cast would be equal to
> "dynamic_cast<X*>(this)", and the call would still go to
> wrong destination. That is, do_f would have to be virtual.

Yes  and  no.  I had assumed (but never stated explicitly)  that  a
function using current_class would normally want to be virtual,  so
that  you  can  still get the most-derived version of the  function
when  accessing  an object through a base class pointer.   However,
there  may  also  be cases where you want the base version  of  the
function called when invoking through a base pointer, and it should
probably  be left up to the pattern class writer to decide  whether
he wants a member function to be virtual or not.

> But even if it were virtual, only the linker could build the derived
> class's virtual function table, because the compiler doesn't
> know that X::do_f() has used current_class until it has seen
> the definition, which might be in different translation unit from
> all derived classes.

That's  exactly  why it can't work that way; the entire  definition
must be in the same translation unit, for that's how templates work
-- and to use current_class is to use a template (just one that you
happen  to  never  need  to  fill  in  manually,  that's  the  only
difference).



-------------------------------------------------------------------
SUMMARY


For  the  price of a single keyword (which is treated as a  special
implicit   template  if  used  and  consistently  follows  existing
template and other rules), we gain an enormous advantage in writing
reusable  code:  full generic patterns.  However, the  use  is  not
limited to patterns only; it is useful in many cases where we would
like  to transparently mix in behaviour from base classes, such  as
the   ProtectedObject   in  Example  4.   Partial   (and   fragile)
workarounds  exist that work in some cases, but they fail  in  even
normal  use,  forcing us to choose between mixins and polymorphism,
to  remove mixin member functions from their mixin classes, or make
other  unreasonable tradeoffs.  The functionality supplied  by  the
proposed   current_class  keyword  is  already  present  in   other
languages; a strong indication of both need and viability.

Please  consider this proposal.  I am personally convinced  of  its
merits - indeed, of its necessity - and I hope I have been able  to
clearly present some of the reasons why this is a simple, powerful,
and  essential language feature for a popular and growing style  of
generic programming.

Regards,  and thanks for all the hard work you've all  already  put
into this standard on our behalf,


  Herb Sutter
  herbs@interlog.com


Connected Object Solutions
2228 Urwin, Suite 102   Oakville ON Canada   L6L 2T2
Tel.: (416) 618-0184   Fax: (905) 847-6019
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter                 2228 Urwin, Suite 102       voice (416) 618-0184
Connected Object Solutions  Oakville ON Canada L6L 2T2    fax (905) 847-6019


  =========================================================================

  Public Review Comment T28

  T28 Core

  _____________________________________________________________________________
  To: X3SEC
  From: Nigel Chapman on Fri, Jul 14, 1995 6:27 PM
  Subject: C++ draft standard

  Because of technical and other problems, this message didn't get sent when
  I composed it, but you should have got a paper version of it.  It isn't
  exactly an earth-shattering matter, but I hope, even if I missed the ANSI
  meeting, it can be passed on to the appropriate person.

  Dear Ms Donovan,

  I write to draw your attention to an inconsistency of presentation in the
  C++ draft standard.  In section 12.4, paragraph 9, we read ``Destructors
  are invoked implicitly (1) when an automatic variable or temporary object
  goes out of scope''. However, in section 3 the authors go to some length to
  define a scope as a ``portion of program text''.  It only makes sense to
  refer to where a name goes out of scope, not when an object does.  This
  sentence should presumably be rewritten in terms of the concepts of storage
  duration and lifetime, defined in sections 3.7 and 3.8.  Interestingly,
  although
  the formulation in terms of scope appears in the ARM, a correct version
  is given in `The C++ Programming Language, 2nd edition'' p170.

  Although I hardly think this needs to be registered as a formal comment, I
  will nevertheless send a hard copy, in case that is necessary to bring it
  to the notice of the editor.

  Nigel Chapman


=========================================================================

  Public Review Comment T29

  T29 Library

_______________________________________________________________________________
To: X3SEC
From: vandevod@cs.rpi.edu on Wed, Jul 5, 1995 7:56 PM
Subject: Comment on ISO/IEC CD 14882: <valarray>

To whom it may concern,

Included in this mail is a commentary/proposal concerning the
<valarray> header.

Signed hardcopies will also be sent to the appropriated addresses.

Sincerely,

David Vandevoorde
_______________________________________________________________________________

                Comments on the proposed <valarray> header
                ==========================================

                            David Vandevoorde
                     Department of Computer Science
           Rensselaer Polytechnic Institute  -  Troy, NY12180
                           vandevod@cs.rpi.edu

Abstract
--------
This document argues for a revision of the specifications for a numerical
array type template as described in the draft C++ standard (ISO/EIC CD 14882).
The principal motivations for the proposed changes are: (in no particular
order)
     * architecture independence. It is felt that the current specifications
       limit the usability of implementations for several architectures with
       a potential for many-at-a-time operations.
     * implementation flexibility and efficiency. It is not clear how the
       current specifications of the valarray<T> types can be implemented
       efficiently on traditional monoprocessor architectures without very
       special treatment by the language processor.
     * consistency. Valarray<T> types may benefit from an interface similar
       to certain other parts of the standard C++ library (especially the
       vector class templates).

1. Introduction
---------------
Chapter 26 of the draft C++ standard introduces a number of tools aimed at
supporting numerical data structures and algorithms. Among them is the
numerical array class template valarray<T> and a set of related functions
and classes. This author identifies the following issues that make this
inclusion in the standard C++ library worthwhile:
     * limited aliasing. A processor of the C++ language may assume that
       elements of a valarray<T> object are not modified without access to
       specific methods of that object.
     * multiprocessing. The <valarray> header introduces elementwise
       operations that may be computed concurrently. Certain reduction
       operations may also benefit from parallel processing.
     * standardization. If the proposed types and functions can be implemented
       efficiently on a wide variety of platforms, their standard availability
       may promote the development of portable high-performance numerical C++
       software.
The draft standard specifies that the valarray functionality may be
implemented as a regular template library or be treated specially by the
compiler. However, it is not clear how an efficient implementation can be
achieved without requiring that special treatment. In particular, one must
cope with the functional character of overloaded operators that potentially
lead to extraneous temporaries. For example, operator+ for valarray<T> has
the following prototype:

  template<class T>
     valarray<T> operator+(const valarray<T>&, const valarray<T>&);

and thus must return a temporary valarray. Suppose this is implemented without
relying on specific support of the compiler. A smart referencing mechanism
could be used inside the valarray<T> class to avoid the overhead of allocating
unnecessary space for the result, but that will most probably seriously affect
the size and performance of the resulting executable code. Allocating space to
hold the temporary result of the array addition is, however, equally
unacceptable and also likely to result in poor performance.
The requirements of the current definition of operator T*() imply that the
numerical array is stored in memory that obeys the C memory model.
Unfortunately, many architectures that are equipped to handle "n-at-a-time"
operations do so on data that is laid out in memory that does not conform to
this model. Despite this conformance to the C memory model, the valarray<T>
syntax revealing this structure is somewhat inconsistent with the vector<T>
syntax that exhibits analogous properties.
Finally, the requirement to support non-conforming assignments seems a
gratuitous sacrifice of performance for a tool that is supposed to help
portably exploit the achievable performance of a given architecture.

2. Proposed Changes
-------------------
Probably the simplest way to address the above concerns is to simply abandon
the standardization of a numerical array. Indeed, it may well prove more
suitable to standardize more fundamental concepts such as the specifications
for lack of aliasing, and leave the development of de facto standards for
fundamental numerical structures to third parties. This is after all what
happened with Fortran 77 which is probably the most successful "compiler-
oriented language" for numerical work. Given that the "more fundamental
concepts" seem unlikely to make it into the standard at this point, we propose
the addition of a second template argument to valarray<T>:

  template<class T, class S = classic_array> // S = storage model
  class valarray;

The storage model S can specify that the array be stored in memory obeying the
traditional C memory model (S = classic_array) or a more exotic model. It can
also specify an implicit model to avoid the formation of large or complex
temporaries. Thus the specification of operator+ may be expressed as follows:

  template<class T, class S1, class S2>
  valarray<T, X> operator+(const valarray<T, S1>&, const valarray<T, S2>&);

where X is an implementation dependent type (quite possibly holding references
to the valarray objects being summed so that the actual summation may be
delayed until destination storage is determined by a constructor invocation or
an assignment operator). To allow for portable "n-at-a-time" operations where
available, it seems judicious to mandate an additional storage model (say,
computational_array or comp_array) that may be a synonym (e.g., through
typedef) of classic_array if no special support for parallelism is intended.
The next most important advocated change is probably the requirement for
conforming assignment: a valarray object x can be assigned to a valarray y
only if both have the same "size". The current allowance of non-conforming
assignments has been found to require more code and registers on traditional
architectures even when the assignment is actually conforming. It is also a
suspect for poor optimization when valarray assignments are specified in the
body of loop constructs.
We propose a few more (mostly syntactical) changes in the interest of
consistency. For example, we keep the member function names of the vector
template when the functionality matches. We also rework the concept of
"mapping" and introduce its cousin "reduction" in a way that seems more
consistent with the expectations of a standard.
In the following section, an attempt is made to semi-formally describe the
newly proposed interfaces. Section 4 follows with several clarifying notes.

3. New Specifications
---------------------
namespace std {
  class classic_array;  // Both types may be synonymous to other types and/or
  class comp_array;     // to each other.

  template<class T, class S = classic_array>
  class valarray {
  public:
     // These following must exist for S = classic_array or comp_array
     // For other storage models they are optional
     valarray();                   // Construct array of size 0
     explicit valarray(size_t);    // Unitialized array of given size
     valarray(const T& v, size_t);
                         // Array of given size, initialized to all v's
     template<class S1> valarray(const valarray<T, S1>&);
                         // Must support S1 = classic_array, comp_array and
                         // any storage model returned by the implementation
     ~valarray();
     void size(size_t);  // Change size from 0 to given argument
     void free();        // Change size to zero

     // The following must be supported for S = classic_array, comp_array
     // and implementation dependent types denoted by Wn in what follows
     // For other storage models they are optional
     template<class S1> valarray<T, S>& operator=(const valarray<T, S1>& b);
                         // An implementation may assume size() == b.size()
     template<class S1> valarray<T, S>& operator*=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator/=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator%=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator+=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator-=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator^=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator&=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator|=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator<<=(const valarray<T, S1>&);
     template<class S1> valarray<T, S>& operator>>=(const valarray<T, S1>&);
                         // Must support S1 = classic_array, comp_array and
                         // any storage model returned by the implementation
     valarray<T, S>& operator=(const T&);
                         // Instead of "fill()"; for consistency
     valarray<T, S>& operator*=(const T&);
     valarray<T, S>& operator/=(const T&);
     valarray<T, S>& operator%=(const T&);
     valarray<T, S>& operator+=(const T&);
     valarray<T, S>& operator-=(const T&);
     valarray<T, S>& operator^=(const T&);
     valarray<T, S>& operator&=(const T&);
     valarray<T, S>& operator|=(const T&);
     valarray<T, S>& operator<<=(const T&);
     valarray<T, S>& operator>>=(const T&);

     template<class S1>
       valarray<T, W1> operator[](const valarray<size_t, S1>&);
                         // Must support S1 = classic_array, comp_array and
                         // any storage model returned by the implementation

     // The following members must exist for any storage model returned by
     // the implementation (including classic_array and comp_array)
     size_t size() const;  // Instead of length(); consistent with vector<T>
     T operator[](size_t) const;
     template<class S1>
          valarray<T, R1> operator[](const valarray<size_t, S1>&) const;
                         // Must support S1 = classic_array, comp_array and
                         // any storage model returned by the implementation

     // The following members must exist for S = classic_array
     // For other storage models they are optional
     T* begin();              // The begin() and end() members are analogous
     const T* begin() const;  // to those of vector<T>
     T* end();
     const T* end() const;
     T& operator[](size_t);

     // This member must exist for S = comp_array or any implementation
     // dependent type denoted by Wn in this description (Wn = W1, W2, ...)
     // Type E1 must be convertible to type T and assignment of a T object
     // to the object of type E1 returned by a call to operator[] must result
     // in the corresponding modification of the array element addressed
     // E.g., "T&" may be an acceptable type for E1
     E1 operator[](size_t);

     // Other implementation dependent declarations are allowed
  }; // End template class valarray<T, S>

  // Elementwise conversion
  template<class T1, class T2, class S1>
     valarray<T2, R2> val_cast<T2>(const valarray<T1, S1>&);

  // Subset selections: simple slicing, generalized slicing and masking
  // The versions returning a valarray with storage model Wn must only be
  // supported for S1 = classic_array, comp_array or Wn
  template<class T1, class S1>
     valarray<T1, R3> slice(const valarray<T1, S1>&, size_t start,
                            size_t length, size_t stride = 1);
  template<class T1, class S1>
     valarray<T1, W2> slice(valarray<T1, S1>&, size_t start,
                            size_t length, size_t stride = 1);
  template<class T1, class S1, class S2, class S3>
     valarray<T1, R4> slice(const valarray<T1, S1>&, size_t start,
                            const valarray<size_t, S2>& lengths,
                            const valarray<int, S3>& strides);
  template<class T1, class S1, class S2, class S3>
     valarray<T1, W3> slice(valarray<T1, S1>&, size_t start,
                            const valarray<size_t, S2>& lengths,
                            const valarray<int, S3>& strides);
  template<class T1, class S1, class S2>
     valarray<T1, R5> mask(const valarray<T1, S1>&, const valarray<bool, S2>&);
  template<class T1, class S1, class S2>
     valarray<T1, W4> mask(valarray<T1, S1>&, const valarray<bool, S2>&);

  // Unary operators
  template<class T1, class S1>
     valarray<T1, R6> operator+(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R7> operator-(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R8> operator~(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<bool, R9> operator!(const valarray<T1, S1>&);

  // Binary operators (arithmetic)
  template<class T1, class S1, class S2>
     valarray<T1, R10> operator*(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R11> operator*(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R12> operator*(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R13> operator/(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R14> operator/(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R15> operator/(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R16> operator%(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R17> operator%(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R18> operator%(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R19> operator+(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R20> operator+(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R21> operator+(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R22> operator-(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R23> operator-(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R24> operator-(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R25> operator^(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R26> operator^(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R27> operator^(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R28> operator&(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R29> operator&(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R30> operator&(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R31> operator|(const valarray<T1, S1>&,
                                 const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R32> operator|(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R33> operator|(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R34> operator<<(const valarray<T1, S1>&,
                                  const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R35> operator<<(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R36> operator<<(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<T1, R37> operator>>(const valarray<T1, S1>&,
                                  const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<T1, R38> operator>>(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<T1, R39> operator>>(const T1&, const valarray<T1, S1>&);

  // Binary operators (logical)
  template<class T1, class S1, class S2>
     valarray<bool, R40> operator&&(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R41> operator&&(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R42> operator&&(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R43> operator||(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R44> operator||(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R45> operator||(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R46> operator==(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R47> operator==(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R48> operator==(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R49> operator!=(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R50> operator!=(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R51> operator!=(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R52> operator<(const valarray<T1, S1>&,
                                   const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R53> operator<(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R54> operator<(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R55> operator>(const valarray<T1, S1>&,
                                   const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R56> operator>(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R57> operator>(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R58> operator<=(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R59> operator<=(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R60> operator<=(const T1&, const valarray<T1, S1>&);
  template<class T1, class S1, class S2>
     valarray<bool, R61> operator>=(const valarray<T1, S1>&,
                                    const valarray<T1, S2>&);
  template<class T1, class S1>
     valarray<bool, R62> operator>=(const valarray<T1, S1>&, const T1&);
  template<class T1, class S1>
     valarray<bool, R63> operator>=(const T1&, const valarray<T1, S1>&);

  // Reductor functions
  template<class T1, class S1, class Fr>
     T1 reduce<Fr>(const valarray<T1, S1>&);
  template<class T1, class S1>
     T1 sum(const valarray<T1, S1>&);
  template<class T1, class S1>
     T1 product(const valarray<T1, S1>&);
  template<class T1, class S1>
     T1 min(const valarray<T1, S1>&);
  template<class T1, class S1>
     T1 max(const valarray<T1, S1>&);

  // Unary functions
  template<class T1, class S1, class Fu>
     valarray<T1, R64> apply<Fu>(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R65> abs(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R66> acos(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R67> asin(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R68> atan(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R69> cos(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R70> cosh(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R71> exp(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R72> log(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R73> log10(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R74> sin(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R75> sinh(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R76> sqrt(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R77> tan(const valarray<T1, S1>&);
  template<class T1, class S1>
     valarray<T1, R78> tanh(const valarray<T1, S1>&);

  // Binary functions
  template<class T1, class S1, class T2, class S2, class Fb>
     valarray<T1, R79> apply<Fb>(const valarray<T1, S1>&,
                                 const valarray<T2, S2>&);
  template<class T1, class S1, class S2>
     valarray<T1, R80> atan2(const valarray<T1, S1>&,
                             const valarray<T1, S2>&);
  template<class T1, class S1, class S2>
     valarray<T1, R81> max(const valarray<T1, S1>&,
                           const valarray<T1, S2>&);
  template<class T1, class S1, class S2>
     valarray<T1, R82> min(const valarray<T1, S1>&,
                           const valarray<T1, S2>&);
  template<class T1, class S1, class S2>
     valarray<T1, R83> pow(const valarray<T1, S1>&,
                           const valarray<T1, S2>&);

  // Topological functions
  template<class T1, class S1>
     valarray<T1, R84> shift(const valarray<T1, S1>&, int);
     valarray<T1, R85> cshift(const valarray<T1, S1>&, int);
     valarray<T1, R86> reverse(const valarray<T1, S1>&);
} // End namespace std

4. Notes
--------
1. In Section 3, storage models denoted by "Wn" (W1, W2, ...) are meant to be
   "writable"; i.e., a user might change the value of some of the elements of
   a valarray<T, Wn> object. Storage models denoted by "Rn" can be "read-only"
   or even refer to "implicit storage"; however, it may equally well be some-
   how "writable". Indeed, an implementation can, if it so wishes, recognize
   only a single storage model; in that case classic_array and comp_array are
   synonymous and the Rn and Wn specifiers stand for one of these synonyms.
   It is equally legal to have classic_array and/or comp_array be synonyms of
   a built-in type. The types denoted by Wn and Rn may also depend on the
   template arguments.
2. It is not required that a user be able to specify her/his own storage
   model. The concept of a storage model is mainly intended to help the
   implementer exploit the power of a target architecture.
3. The templated "copy-constructor" has true copy semantics (as prescribed in
   the current draft).
4. Since only conforming assignment is allowed, a new mechanism to support
   size modification issues was introduced. In the interest of simplicity
   (and probably efficiency) size(size_t) only needs to support resizing from
   a length that is zero. This is different from vector<T>::resize() and thus
   the syntax was made different as well. The member free() is the complement
   to size(size_t) and is already present in the current draft.
5. Allowing "computed assignments" with scalars, yet not allowing the assign-
   ment of a scalar seemed totally counter-intuitive. The fill() member was
   thus dropped in favor of the assignment operator.
6. The subscripting operator (operator[]) requires some special attention.
   Since comp_array does not have to denote a traditional C memory model,
   it should not be required to return a reference to one of its elements.
   Yet is seems useful to be able to read or write individual elements of
   such an array; hence the unspecified type E1. One should probably also
   allow an implementation to return a const T& reference rather than a T
   object whenever more convenient. In particular, a valarray of valarray's
   should not suffer from excessive copying.
7. The val_cast<T2> function is intended to allow mixed precision operations
   without needless temporaries.
8. The slicing and masking operation have been made regular functions. This
   was chosen only on the basis of personal experience with a prototype
   implementation. The chosen interface eliminates the need for slice and
   gslice types. This may be a minor issue.
9. Reduction functions (min, max, sum) and topological functions have been
   made regular functions as a matter of consistency. A reduction function
   product, a topological function reverse and binary functions min and max
   (different from the one-argument reductions with the same name) were added
   for the sake of completeness.
10. A generic reduction function template reduce has been introduced to match
    the mapping function apply (which has also become a regular function).
    Both have been made more flexible by introducing a "functor" template
    argument instead of a "function pointer" function argument.
    The function pointer can of course still be encapsulated in a functor.
    A functor is a class which overloads operator(). The reduce function
    requires a functor Fr that takes two scalar (type T1, T1& or const T1&)
    arguments and returns a single scalar; an implementation may assume that
    the embodied operation is commutative and associative. The apply function
    comes in two variants to handle one (functor Fu) or two (functor Fb)
    valarray arguments. Functor Fu should take one scalar argument and return
    a value of a similar type; functor Fb should accept two scalar arguments
    and return a scalar result.
11. A prototype implementation of these ideas is being developed by the
    author. An earlier version is available by anonymous ftp from
    ftp://ftp.cs.rpi.edu/pub/vandevod/Valarray.


=========================================================================

  Public Review Comment T30

  T30 Core
  T30.1 Tom Plum
  T30.2 Tom Plum
  T30.3 Tom Plum
  T30.4 Extension
_______________________________________________________________________________
Date: Thu, 20 Jul 95 17:48:00 -0500
From: dqualls@ocdis01.tinker.af.mil (GS-12 David Qualls)
Subject: public comment on C++
To: x3sec@itic.nw.dc.us

from:    David Qualls                                    20 July, 1995
         16704 Triple XXX Circle
         Choctaw, Ok  73020-3905

to:      X3J16 Committee Secretariat
         Attn: Lynn Barra
         1250 Eye Street, Suite 200
         Washington, DC 20005

subject: Public Comments on the C++ language.

	 Please find enclosed, my comments regarding the C++ language
	 standard.

	 Thank you for your attention to these comments.



	 Sincerely,


	 David Qualls.
#012#
I've had difficulty downloading and reading the C++ working paper,
principally because it is not in an ASCII file.  Therefore, I've
been unable to see if my concerns have already been addressed, or
even to reference a paragraph number to which your attention should
be addressed.

Therefore, I'm basing the following statements on "The C++ PROGRAMMING
LANGUAGE"  (referred to in this paper as "the book") Second Edition, by
Bjarne Stroustrup.

-----------------------------------------------------------------------------
## T30.1 ##

Subject:    Preprocessor, macro expansion, escape sequences.

Question:   Are (character) escape sequences given their meaning
	    during macro expansion?  I don't feel the book is
	    clear on this issue.

Example:    #define  remove_tail( statement )   statement ## \b\b\b
	    remove_tail( printf("stuff"); ) %d\n", int_var);

	    Does this work as expected (per the standard)?  That is,
	    does it expand (per the standard) to
	    printf("stuff%d\n", int_var);

Comments:   The couple of compilers I've tested do not interpret it
            this way.  Enabling the pre processor this way would greatly
	    increase it's capability.  We would (and this would be nice
	    ANYWAY) also need an escape sequence for a simple forward
	    space.  In the example above, we can't separate the "stuff"
	    from the %d without it.  Note: I ran squarely into this
	    question while attempting to write an ANSI C conforming
	    preprocessor: the book was not clear.

-----------------------------------------------------------------------------
## T30.2 ##

Subject:    Preprocessor, line continuation with '//' comments.

Question:   The book is not explicitly clear as to how the // comments,
	    and the '\''\n' interact.  Does the // comment terminate
	    with the '\' <newline> combination or not.

Example:    #define  comment_question( arg )                   \
	       global_var1 = arg % 7 // this won't work with   \
	       global_var2 = arg / 7 // my primary compiler!

	    #define  same_question( arg )                      \
	       global_var1 = arg % 7 /* this DOES work, but */ \
	       global_var2 = arg / 7 /* is not nestable.    */

	    /* History: an earlier version of 'same_question'
	    #define  same_question( arg )                      \
	       global_var1 = arg % 6 /* OOP'S. This really  */ \
	       global_var2 = arg / 6 /* goes afoul! Nesting NOT ALLOWED!*/
	    */

Comments:   Based on the examples above, it's obvious to me that
	    during preprocessing, comment termination should occur
	    BEFORE line concatenation.  Having line concatenation
	    occur before comment termination leaves no way to embed
	    comments within macros that might later need to be
	    commented out.  The book says that line concatenation
	    precedes comment removal, but r.2.2 SEEMS TO SAY that
	    // comments should terminate on the PHYSICAL line they
	    appear on, not the extended line (some interpretive reading
	    between the lines there).  Again, I first ran into this
	    while trying to make my own ANSI preprocessor work with
	    the // style comments.  Please make this rule explicit.

-----------------------------------------------------------------------------
## T30.3 ##

Subject:    Preprocessor, possible ANSI C extension to allow empty args

Question:   If C++ is to remain a superset of C, then would it not be
	    wise to incorporate the features which the next revision of
	    ANSI C is likely to incorporate?

Comments:   One possible new addition to C will be the ability for the
	    preprocessor to permit empty parameters within macro calls.

-----------------------------------------------------------------------------
## T30.4 ##

Subject:    C(++) as a "portable assembler"

Note:       This one is my 40 pound soap box!

Commentary: I laugh every time I read where someone refers to C (or C++)
            as a portable assembler.  It's NOT!  It's definitely not an
	    assembler, and it's not terribly portable.  It is not an
	    assembler because the language lacks a direct way to do
	    indexed local jumps.  I'm only familiar a couple of assembly
	    languages, but I sure thought that indexed local jumps were
	    a part of every assembler.  That is, the ability (within a
	    procedure) to jump to a code location specified within
	    another register or memory location.

	    jmp[cd_ptr]    ;execution jumps to where cd_ptr is pointing.

	    C(++) is not very portable either because the standard
	    headers contain no standard macros addressing how integers
	    and structures are stored and accessed on varying platforms.

	    The issue of indexed local jumps could be easily fixed in
	    C(++) by allowing pointers to labels.
#012#
Example:    void  example( int arg )
	    {
	      label *lPtr[3] = { LABEL1, LABEL2, LABEL3 };
	      /* 'label' is a new keyword. In the classic C sense, the  */
	      /* label name is really a pointer to a code location.     */
	      /* C(++) already permits forward referencing in this      */
	      /* sense.  That is, you can 'goto' a label that hasn't    */
	      /* been previously declared.  Some environments insist on */
              /* defaulting to 'const' to prohibit self modifying code. */

	      arg = func( arg );
	       /* arg gets distorted in a way that's   */
	       /* too complex for the compiler to be   */
	       /* able to predict all possible values. */
	
	      goto lPtr[ arg ]; /* The code author understands */
				/* the possible values.        */

	      LABEL1: /* do some stuff */
	      LABEL2: /* do some stuff */
	      LABEL3: /* do some stuff */

	      return;
	    }

Comments:   I admit that in the example above, a switch/case statement
	    would do the trick.  The problem with switch is that some
	    compilers simply convert switch statements into a long line
	    of very slow running if statements.  In some cases, as I've
	    tried to allude to above, the compiler simply can't
	    understand what possible values the arg may take on, and
	    thus is forced into translating the code into if statements.
	    It'd be incorrect translation to do otherwise!
	
	    The real utility of this proposed construct is when the
	    code writer KNOWS the possible values the index can assume
	    and the compiler simply can't figure them out.  I have
	    been very frustrated (and I suspect, so have a lot of other
	    performance hounds who default to writing in assembler) by
	    the lack of indexed local jumps in the C(++) language.


	    Now regarding portability.  In order to take advantage of
	    the low level tools which C(++) provides for us, we need a
	    whole suite of portability macros for the integers.  I'm
	    not sure we can do much with the floating points since they
	    are allowed to change representation while running.
#012#
Example:    #define  CHAR0INSHRT   1  /* least significant char in a     */
				      /* short when the short is treated */
				      /* as an array of chars.           */
	    #define  CHAR1INSHRT   0  /* next most significant */

	    #define  CHAR0INLONG   7  /* least significant char in long */
	    #define  CHAR1INLONG   3  /* next most significant */
	    #define  CHAR2INLONG   5  /* even more significant */
	    #define  CHAR3INLONG   1  /* continuing in significance */
	    #define  CHAR4INLONG   6  /* ditto */
	    #define  CHAR5INLONG   2  /* ditto */
	    #define  CHAR6INLONG   4  /* ditto */
	    #define  CHAR7INLONG   0  /* most significant char in long */
	
	    /* macro to access the N'th least significant char in a long */
	    #define  NthCHARINLONG( N, longarg )    \
			 *((char*)(&longarg) + CHAR ## N ## INLONG)

            Make similar macros for all the other integer types.
	
	    If it's decided that significance should be indicated in a
	    different order, just reverse the order.

	    If an environment won't support such disection of the larger
	    types, then just don't define them.

	    We also need similarly clever macros which indicate how the
	    various types align within structures, which bit (least
	    or most significant) is the sign bit, is zero represented by
	    all bits set to zero or something else, how bitfields are
	    ordered, as well as any other environment specific issues,
	    including everything which the standard defines as
	    "implementation dependent".  A full suite of these
	    macros will make portable programming a MUCH easier job.


=========================================================================

  Public Review Comment T31

  T31 Core

_______________________________________________________________________________
To: X3SEC
From: ajay@lehman.com (Ajay Kamdar)
Date: Mon, 24 Jul 1995 14:23:58 -0400
To: x3sec@itic.nw.dc.us
Subject: Public comment on ISO/IEC CD 14882
Cc: ajay@lehman.com


Please register the following comment and proposal to modify the emerging C++
standard. I have mailed hard copies of the comment to both the X3 Secretariat
and to ANSI.

Thank you.

- Ajay


Proposal
========

Make the destructor of a class implicitly virtual if the class has any other
virtual functions.

Required modifications to the text of the CD
============================================
[ Addition to existing text is marked with ^^^^^]


    12.4 Destructors     [class.dtor]

    6. Destructors are not inherited. A destructor can be declared virtual
       or pure virtual; a destructor is implicitly virtual if the class has
			^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       any other virtual functions; if any objects of that class ...
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^

Discussion
==========

*) Forgetting to make the destructor of a polymorphic class virtual is a
   common mistake made both by inexperienced and experienced C++ programmers.
   This makes it harder to use the language, and the resulting problems are
   often difficult to debug and fix. Accepting this proposal eliminate an
   unnecessary source of errors.

*) There are no backward compatibility issues to worry about. The behavior
   of deleting an object using a pointer to a static type without a virtual
   destructor is currently specified to be undefined if the dynamic type of
   the object is different from the static type.

*) There is no reason for wanting *not* to execute all the appropriate
   destructors.

*) There would be no change to the layout of an object because the destructor
   would be made implicitly virtual only if the class had at least one other
   virtual function.

*) A (positive) side effect of the change would be that existing erroneous
   code which currently has undefined behavior would start behaving properly.

*) It would be very easy to modify compilers to implement the new behavior.



--
Ajay Kamdar        |    Email: ajay@lehman.com    |    Standard Disclaimer
Lehman Brothers    |    Phone: (201) 524-5048     |

=========================================================================

  Late Public Review Comment T32

  T32 Core

_______________________________________________________________________________
To: X3SEC
Date: Fri, 28 Jul 1995 13:30:36 -0700
From: scotts@ims.com (Scott Schurr)
To: x3sec@itic.nw.dc.us
Subject: Comment regarding exception specifications

Dear Secretariat,

I realize that the period for official comment on the C++ Draft
Standard is past.  Nevertheless, I'm sending you this comment in the
hopes that other parties may have expressed the same concern and that
this comment will lend weight to theirs.

We at IMS are looking at starting a large project using C++.  With the
advent of exceptions in the draft standard, we've been considering
their use.  Exceptions seem appealing for a couple of reasons:

  1) They provide a simple solution for a function that returns a
     reference to an object.  If the requested object was not
     available or could not be constructed for some exceptional
     reason, then an exception can be thrown.

     Otherwise, without exceptions, some sort of empty object must be
     returned (which must be special cased by every member function).

  2) Exceptions reduce the need for error code checking upon return
     from a function.

We are concerned, however, that introducing exceptions into our code
base will make our code more fragile, rather than more robust.  We
believe that robust code is generated when errors are caught at
compile time.  Coding errors caught at run time make the code fragile.

One way to make exception handling more robust is to provide exception
specification checking at compile time.  If all functions in a system
have exception specifications, then the compiler can check the
exception specification of all called functions to see if they throw
exceptions that are not caught by the caller and are allowed to be
thrown by the caller.

Function prototypes which do not include an exception specification
should generate a compiler warning (with an appropriate compiler
switch).

There are two things in the current draft standard which lead me to
believe that the previous paragraphs do not describe the intention of
the language specification.  These are sections 15.4 - 9 and
15.4 - 11.  15.4 - 9 says:

  Whenever an exception is thrown and the search for a handler (15.3)
  encounters the outermost block of a function with an
  exception-specification, the function unexpected() is called
  (15.5.2) if the exception- specification does not allow the
  exception.  [Example:

    class X { };
    class Y { };
    class Z: public X { };
    class W { };

    void f() throw (X, Y)
    {
        int n = 0;
        if (n) throw X();  // OK
        if (n) throw Z();  // also OK
        throw W();         // will call unexpected()
    }

  --end example]

This implies to me that the problem with "throw W()" not complying
with the exception specification will only be caught at run time --
not at compile time.  This is unacceptable if we are trying to build a
robust system that does not abort at a customer site.

In addition to the surprise in section 15.4 - 9, there is an
additional surprise in section 15.4 - 11.  It reads as follows:

  An implementation shall not reject an expression merely because
  when executed it throws or might throw an exception that the
  containing function does not allow.  [Example:

    extern void f() throw(X, Y);

    void g() throw(X)
    {
        f();               // OK
    }

  the call to f is well-formed even though when called, f might throw
  exception Y that g does not allow.  ]

Which means (at least the way we read it) that compiler writers are
*not*allowed* to check for violations to the exception specification.
All such violations may only be caught at run time.  To say the least
this is counter-intuitive.

In summary, for exception specifications to be useful to us in our
current project, we must be able to check the correctness of the
exception system construction before runtime.  Optimally, this would
be at compile time.  As far as we can tell, the current exception
definition prevents compiler writers from checking for properly
constructed exception hierarchies.

We believe that this is a flaw in the language specification that
should be corrected.

There remains the question of whether a compiler writer could viably
check exception handling specifications at compile time.  We asked
this question of Cygnus (one of our compiler vendors).  The response
came back from Mike Stump that this was a reasonable and feasible
thing to do (in Cygnus problem report "g++/7556: Question regarding
C++ ANSI Draft Standard").  So at least one compiler vendor feels that
exception specification checking at compile time, with our suggested
changes, is feasible.

--------------------------------------
Scott Schurr
  Integrated Measurement Systems, Inc.
  Voice: (503) 626-7117
  Fax:   (503) 644-6969
  Email: scotts@ims.com
--------------------------------------

=========================================================================

  Late Public Review Comment T33

  T33 Library

_______________________________________________________________________________
To: X3SEC
From: Jack Reeves <jack@fx.com>
Subject: C++ Standard Library comments
To: x3sec@itic.nw.dc.us
Date: Thu, 27 Jul 95 17:33:58 PDT
Cc: jack@fx.com

					Jack W. Reeves
					Dow Jones Telerate Systems Inc.
					2465 Faber Place
					Palo Alto, CA 94303
X3 Secretariat
Attention: Deborah J. Donovan
1250 Eye Street NW, Suite 200
Washington, DC 20005

Comments on the draft C++ Standard.
I have a couple of comments/questions about the C++ Standard -- actually the
strings
library.

1. The function basic_string<>::c_str() is prototyped as
	const charT* c_str() const
The function returns a pointer to an eos() terminated string.  The semantics
are fine,
I just think the prototype is in error.  It think the correct prototype
should be
	const charT* c_str()	// not 'const' function
I will accept that adding a traits::eos() character in the undefined portion
of the
reserved memory outside of the valid string data is philosophically not a
change of the
state of the object and hence can be allowed within a 'const' member
function.
However, adding this 'hidden' character can cause the re-allocation of the
internal
representation, and I draw the line at this silliness:
	void f(const string s)
	{
	    size_t before = s.capacity();  // const function
	    cout << s.c_str() << endl;     // const function???
	    size_t after = s.capacity();   // const function
	    assert(before == after);       // This should never fail!!!
	}
In general, I consider it unacceptable for a 'const' function to cause
changes in
the underlying state of the system irrespective of whether that function
changes the
"contents" of the object as seen through the interface of the abstraction.
As such,
I will accept c_str() as a const member function only if it is defined to
never re-alloc
the internal string.  This could be done of course, by insisting that the
memory reserved
always contains room for the eos(), but I think a better approach is to
simply change
the definition of c_str.  I note that my definition of what is "const" may be
different
from the definition of 'const' as used in the language standard. If so,
please point me to
where the definition is spelled out in the standard.

2. The function basic_string<>::data() is prototyped as
	const charT* data() const
and defined to return a null pointer if size() == 0 otherwise c_str().
I believe this is a mis-wording.  data() should return the appropriate
pointer (or null)
but should not be required to return an eos() terminated string.  There are
two
reasons for this.  (a) If data() does not return c_str() it can truly be a
'const'
member function, and this is good (see 1. above).  (b) Perhaps more
importantly, there
is no need for data() to terminate the string.  In using several different
versions of
string class, most of which come close to the standard, we have never found
it necessary
to have a function that has the semantics as data() is now defined to have.
We have
found many uses for a function ('const' function) that gives access to the
internal
data pointer.  In fact, we use strings in numerous situations where '\0' is a
valid
data element and so terminating such strings is a waste of time since they
are always
dealt with in conjunction with their length().

3. The latest version of the standard adds some new member functions to class
basic_string.
There is now a size() function and several other changes that bring strings
more in parallel
with the newly defined containers.  I have previously pointed out that size()
is defined
in terms of traits::length() which is in turned defined semantically to be
the same as
::strlen().  I feel sure this is an error.  I note that function length() is
defined to be
the same as size().  I presume that length() is retained for compatibility
with previous
versions of string (and may be deprecated in the future).  I wonder if maybe
what was
really desired was that basic_string::length() should return traits::length()
if this
is less than size(), size() otherwise.  I really doubt it, but thought I
would ask.

4. I note that the latest version of the standard changed the order of the
parameters
for one of the constructors from
	basic_string(charT c, size_type n = 1, Allocator& = Allocator())
to
	basic_string(size_type n, charT c, Allocator& = Allocator())
I presume the latter is correct, but wanted to verify.  We have hit at one
occasion
where an older program had
	string s('@', 1);
and this continued to compile correctly with the new header file (we are
using G++), but
silently changed its meaning.

5. I have already suggested the following, but will suggest it again, as I
consider it
important.  Class basic_string has a reserve() function, but no release()
function.
It really needs a release() (or shrink_to_fit()) function.  Partly this is
just good design
(pardon my arrogance) -- the reserve() function is used to indicated an
anticipated increase
in the size of the string, and the release() function is its opposite and is
used to
indicate that no more changes are anticipated and the excess reserved memory
can be given
back to the system.  Partly, reserve() and release() can be used with a
special allocator
that deals with relocatable memory such as the original Macintosh or Windows
-- reserve()
would do a lock and release() could unlock (as well as shrink).
I note two aspects about release().  The first is that it could interact
somewhat poorly
with c_str().
	void f(string s)
	{
	    s.release();                 // shrink to fit
	    cout << s.c_str() << endl;   // trying to re-alloc the string to
size()+1
	                                 // might cause it to have quite a bit of
	                                 // slop
	}
I would consider this annoying, but something that could be lived with.
However,
an alternative provides a solution to my desire for a release() function and
this
problem -- redefine the semantics of reserve() to allow it to function as a
release()
function also.  Thusly -
	after reserve(size_type n) ::=
	   if (n < size()) then capacity is set to size()
	   otherwise capacity() will equal n.
Frankly, this would be my preference.  Thus the example above would become
	void f(string s)
	{
	    s.reserve(s.size()+1);
	    cout << s.c_str() << endl;
	}
with the assurance that the actual memory used is the minimum necessary.
The reserve() function could be prototyped as
	void reserve(size_type res_arg = 0)
where the default argument would allow the use of
	s.reserve() to be semantically equivalent to shrink-to-fit.
	
6. All of the above discussion about release() applies equally to the
vector<>
class.  In fact, I like the new reserve() idea so much I think I'll go
implement it in our string and STL libraries and let you know how it
comes out.  Let me know what you think.

Thanks for your consideration and/or information.
Hardcopy to follow.

Jack Reeves

=========================================================================

  Late Public Review Comment T34

  T34 Extensions

_______________________________________________________________________________
To: X3SEC
Date: Tue, 1 Aug 1995 21:22:35 -0400
From: JonHoyle@aol.com
To: x3sec@itic.nw.dc.us
Subject: C++ suggestions

I was hoping to send you this before the July 25 deadline, but I was unable
to because of a family crisis.  I have two suggestions to be considered for
the langauge specification:

1.  For templates, allow switching on the type.  For example:

template <class T>
void SomeFunction(T theObject)
{
        switch (T)
        {
                case int:
                case short:
                        DoSomething();
                        break;

                case double:
                case anotherType:
                        DoSomethingElse();
                        break;

                default:
                       DoEverythingElse();
                       break;
        }
}

This allows for fine tuning in templated functions.


2.  Define an operator @ as an additional binary operator that can be used
for operator-overloading.  Currently, there is no way to overload an operator
for elementary types.  Now this could allow us to define, say, exponentiation
by:

int  operator@(int x, int y)
{
      if (y == 0) return 1;
      if (y > 0) return x * operator@(x, y-1);
      return (1/x)*operator@(x, y+1);
}


I would also like to commend you on your decision to add a boolean type to
the standard.  Currently, we always run into the problem of defining TRUE as
1, and comparing something that is true but not 1.  For example,

        if (x & 0x007F == TRUE)
        {
               // this always fails
        }

I also like the idea of defaulting templated types:

template <class T = int>
class MyClass<T>
{
     ...  /* etc. */
}

MyClass<> theClass;  // Default type is int

Thank you for taking the time to read my suggestions.  I can be reach at the
following:

Jonathan W. Hoyle
101 Bending Creek Rd.  Apt #2
Rochester, NY  14624
H:  (716) 426-8753
jonhoyle@aol.com

Eastman Kodak Company
W:  (716) 726-0987