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