Topic: Generating new types.
Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/03/24 Raw View
Mark Ping wrote:
>
> Is there any conceived use for non-numeric types? Is this only
> intended for builtin types, or for user-defined types as well?
Yes, it is intended to be used on any type. An example would be to
generate a type based on std::string to represent filenames, or
usernames.
The new type is intended to avoid common errors of passing the wrong
argument or assigning the wrong variable, and to allow overloading of
function and templates. For example, in the following function:
void foo ( const std::string & name, const std::string password );
It is very easy to mix-up the two arguments. The goal is *not* to
replace derivation nor encapsulation. The goal is type safety and type
reuse.
>
> >2.3 Template Specialization
> >
> >Since typegen introduces a new distinc type, template can be specialized
> >over
> >the genrated type, without conflict with the base type. For example:
> >
> > typegen int my_char_t;
> >
> > template ctype<my_char_t> { /* ... */ };
>
> Since all of the examples of usage are numerical types, how will
> numeric_limits<> be handled?
These are the kind of issues I had in mind discussing. I am not aware of
all
the details of all the corners of the standard. I think the
numeric_limits
template could either:
- Be specialized by the user. This seems resonable as the user would
know
what is the valid range for the new type.
- Be declared "friend" as I explained in the last section of my post,
to "bring" it along in the type generation. That is, it would be
automatically defined to be the same as the original type.
You also have to consider what would happen with the alternatives. A
class
or an enum would not have numeric_limits<> defined either.
Note that I do not know if the standard allows overloading
numeric_limits<>
for user-defined types. If it is disallowed, then the standard would
have
either to change or the user would be forced to do without
numeric_limits<>
for user-defined (generated) types.
> >3.3 Class
> >
>
> I think a template class would provide for reuse.
I am not sure what you mean. The original type is not necessarily a
template. I assume that you mean that you would declare a template class
which would somehow encapsulate the original type. How would declare
such a template? With what parameter? Remember, you have to be able to
generate multiple independent types from a single original type.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Maarten Hilferink" <mhilferink@tip.nl>
Date: 2000/03/24 Raw View
> 2. Proposition
>
> ... we propose a new keyword that creates a new type
> from an existing type: typegen. ... For example:
>
> typegen int meter; // OK: new type named meter generated from base int.
Interesting, I have been thinking along these lines to deal with compile
time unit consistency checking on arithmetic expressions.
> ...
> typegen int meter;
> typegen int km;
> typegen km kg;
>
> void foo ( )
> {
> meter m;
> km k;
How would you propose should these expressions be evaluated?
m = m+m;
m= m*m;
m = m+k;
Automatic inheritance from int operator +(int, int ) to meter operator
+(meter, meter)?
It would be a bit cumbersome to have to redefine all arithmetic operators
for a new type,
but then again, that would be safe.
Also,
typegen int meter;
typegen int sqr_meter;
sqr_meter operator *(meter x , meter y ) ( return
static_cast<sqr_meter>(x*y); }
std::val_array<meter> a,b;
std::val_array<sqr_meter> c; c = a*b;
should compile, while a = a*b; should not.
This would be possible if
operator *(val_array<T1>, val_array<T2>)
would make use of a type-definition like:
type_traits<T1,T2>::mutliplication_type to specify its return type.
However, I think the following delivers nearly what you (and I) want without
a new keyword.
template <typename T, int TypeId> struct typegen {
explicit typegen(const T& x) : m_Value(x) {}
T m_Value;
}
const int meterTypeId = 1;
const int kmTypeId = 2;
typedef typegen<int, meterTypeId> meter;
typedef typegen<int, kmTypeId> km;
All your conversion operator stuff is easy to define now.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Hyman Rosen <hymie@prolifics.com>
Date: 2000/03/24 Raw View
"Maarten Hilferink" <mhilferink@tip.nl> writes:
> > typegen int meter; // OK: new type named meter generated from base int.
>
> Interesting, I have been thinking along these lines to deal with compile
> time unit consistency checking on arithmetic expressions.
> > ...
> > typegen int meter;
> How would you propose should these expressions be evaluated?
> m = m+m;
You may want to investigate the specification of subtypes in Ada,
where this sort of thing has been around for ages.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/03/24 Raw View
Maarten Hilferink wrote:
>
> Interesting, I have been thinking along these lines to deal with compile
> time unit consistency checking on arithmetic expressions.
Yes. Currently, I have a tendency to define enum but use them as a kind
of
integer type. For example to define page numbers, or object ID. Of
course, I have to be careful to define the lower and higher bounds to
make sure the compiler select the correct underlying integral type.
> How would you propose should these expressions be evaluated?
>
> m = m+m;
> m= m*m;
> m = m+k;
For integral type, the intention was to bring all operators. I thought I
had wrote so in my OP, but I may have forgotten it. But don't forget
that the intention is to use it for classes also.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: mping@scdt.intel.com (Mark Ping)
Date: 2000/03/24 Raw View
In article <38DA39BA.71AB57D4@artquest.net>,
Pierre Baillargeon <pb@artquest.net> wrote:
>Mark Ping wrote:
>>
>> Is there any conceived use for non-numeric types? Is this only
>> intended for builtin types, or for user-defined types as well?
>
>Yes, it is intended to be used on any type. An example would be to
>generate a type based on std::string to represent filenames, or
>usernames.
>
>The new type is intended to avoid common errors of passing the wrong
>argument or assigning the wrong variable, and to allow overloading of
>function and templates. For example, in the following function:
>
>void foo ( const std::string & name, const std::string password );
>
>It is very easy to mix-up the two arguments. The goal is *not* to
>replace derivation nor encapsulation. The goal is type safety and type
>reuse.
Sure, but any function with >1 arg of the same type can be confused in
the same way. Take a function that uses two args of "meter" -- I'd
think that'd be quite common. I'm doubtful as to how much this would
help.
>> I think a template class would provide for reuse.
>
>I am not sure what you mean. The original type is not necessarily a
>template. I assume that you mean that you would declare a template
>class which would somehow encapsulate the original type. How would
>declare such a template? With what parameter? Remember, you have to
>be able to generate multiple independent types from a single original
>type.
You're right. I did speak too soon and forgot about that requirement.
However, I'm convinced that you could go *very* far in this idea using
class templates.
For instance, something like:
template <class T> numeric_typegen { ... };
class meter : puplic numeric_typegen< int > { ... };
(This should be enough to get the general idea of what I'm saying
across.)
Have you attempted to implement your proposal using existing C++?
What did you find impractical, etc.?
--
The above comments represent my opinion only, and in no way
reflect the opinions or policy of Intel Corp.
Mark Ping mping@scdt.intel.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/03/25 Raw View
Mark Ping wrote:
>
> Sure, but any function with >1 arg of the same type can be confused in
> the same way. Take a function that uses two args of "meter" -- I'd
> think that'd be quite common. I'm doubtful as to how much this would
> help.
Yes, but this at least removes a vast amount of problem.
There is also the possibility of going to the extreme and creating very
specific types. In a function with two meter arguments, either both have
the same semantic and are thus interchangeable, or they have different
semantic which could be integrated in the type. This is very extreme
though.
I wonder if there is any such programming language where all processing
is done in type conversions? A program would then be a succession of
conversions, each of which would do some processing.
> For instance, something like:
>
> template <class T> numeric_typegen { ... };
> class meter : puplic numeric_typegen< int > { ... };
>
> (This should be enough to get the general idea of what I'm saying
> across.)
>
> Have you attempted to implement your proposal using existing C++?
> What did you find impractical, etc.?
The problem I see with your template trick, is that there can easily be
conflicts among libraries and modules. The only thing I "implemented" is
my current tendency to use enums for purpose other than enumeration: to
get an overloadable integral type. For classes, I still derive or
contain.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Claude Qu zel" <Claude_Quezel@Syntell.corba>
Date: 2000/03/25 Raw View
Maarten Hilferink wrote:
> Interesting, I have been thinking along these lines to deal with compile
> time unit consistency checking on arithmetic expressions.
>
This can also be done using code as explained in Scientific and Engineering C++
An Introduction with Advanced Techniques and
Examples de John J. Barton et Lee R. Nackman, Addison Wesley. See code addapted
from this book:
int main()
{
Force f; // Newton == (Kg*m)/(s*s)
Mass m(10); // Kg
Acceleration a(9.8); // m/(s*s)
Speed v(3e8); // m/s
f = m*a; // Ok
f = m*v; // compile time error
return 0;
}
// the solution
template <int Mass, int Length, int Time> // Temp rature, Charge lectrique, ...
class Dimension {
public:
explicit Dimension(double a = 0.0) : m_val(a) {}
double Value() const { return m_val; }
private:
double m_val;
};
// fondamental units
typedef Dimension<1, 0, 0> Mass; // Kg
typedef Dimension<0, 1, 0> Length; // metre
typedef Dimension<0, 0, 1> Time; // seconde
// derived units
typedef Dimension<0, 1, -1> Speed; // l/s == m/s
typedef Dimension<0, 1, -2> Acceleration; // l/(t*t) == m/(s*s)
typedef Dimension<1, 1, -2> Force; // m*l/(t*t) == kg*m/(s*s)
// op rateur *
template<int a1, int b1, int c1, int a2, int b2, int c2>
const Dimension<a1 + a2, b1 + b2, c1 + c2>
operator*(
const Dimension<a1, b1, c1>& lhs,
const Dimension<a2, b2, c2>& rhs
)
{
return Dimension<a1 + a2, b1 + b2, c1 + c2>(lhs.Value() * rhs.Value());
}
--
Claude Qu zel (claude_quezel@syntell.corba)
anti-spam: replace corba by com in private replies
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: mping@scdt.intel.com (Mark Ping)
Date: 2000/03/25 Raw View
[a copy of this message has been posted to: comp.std.c++]
In article <38DB7DC2.8A1CE872@artquest.net>,
Pierre Baillargeon <pb@artquest.net> wrote:
>Mark Ping wrote:
>>
>> Sure, but any function with >1 arg of the same type can be confused in
>> the same way. Take a function that uses two args of "meter" -- I'd
>> think that'd be quite common. I'm doubtful as to how much this would
>> help.
>
>Yes, but this at least removes a vast amount of problem.
Really? Do you have use analyses that show that such an
implementation would in fact remove a significant problem? I'm sure
that in some particular cases it would be very useful, but I can think
of other things that would be very useful in other cases (like a
*true* dynamic cast for instance).
>> For instance, something like:
>>
>> template <class T> numeric_typegen { ... };
>> class meter : puplic numeric_typegen< int > { ... };
>>
>> (This should be enough to get the general idea of what I'm saying
>> across.)
>>
>> Have you attempted to implement your proposal using existing C++?
>> What did you find impractical, etc.?
>
>The problem I see with your template trick, is that there can easily be
>conflicts among libraries and modules. The only thing I "implemented" is
>my current tendency to use enums for purpose other than enumeration: to
>get an overloadable integral type. For classes, I still derive or
>contain.
I don't know what you mean by conflicts among libraries and modules.
You can always have conflicts between libraries and modules. I don't
see any difference between my suggestion and yours as far as this
goes. That is:
//library A
typegen int meter;
...
//library B
typegen float meter;
Seems to be a potential problem. Is this the kind of thing you're
suggesting.
As a side note, I went home last night and spent about 30 minutes and
came up with a template solution that gives all the features you want
except the ability to define implicit conversions (which appears to me
to contradict your original intent anyway). Contact me by email and
we can discuss it further. (Note that this message is being posted to
the newsgroup and sent by email.)
--
The above comments represent my opinion only, and in no way
reflect the opinions or policy of Intel Corp.
Mark Ping mping@scdt.intel.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/03/23 Raw View
I've been keeping the following for a while, but I feel confident enough
to post it now. It's a proposal.
1. Needs
C++ is a strongly typed language. But there is no way to easily reuse an
existing type while:
- enforcing strong-typing (including overloading and templates);
- preserving operability (preserving all capabilities of the original
type);
- allowing easy maintenance (changes in the original type reflected);
- maximizing size and time efficiencies.
2. Proposition
To fulfill all the needs we propose a new keyword that creates a new
type
from an existing type: typegen. The syntax of the keyword is modeled
after
the typedef keyword. Every valid syntax involving the typedef keyword
would
also be valid for the typegen keyword. For example:
typegen int meter; // OK: new type named meter generated from base
int.
2.1 Vocabulary
In the declaration of the type named meter, the type named meter is
called
the generated type. Two generated types with different names are called
distinct types. The type named int is called the original type. The type
named int is also called the base type. These two names only refer to
different types when a typegen uses a generated type as the base type.
Then
the original type refers to the base type of the first generated type.
For
example:
typegen int meter; // OK: int is base and original, meter generated.
typegen meter foo; // OK: int is original, meter base, foo generated.
2.2 Overload
Since typegen introduces a new distinct type, functions can be
overloaded
over the generated type. For example:
typegen int meter;
int foo ( int ) { return 0; }
int foo ( meter ) { return 2; } // OK: function overload.
2.3 Template Specialization
Since typegen introduces a new distinc type, template can be specialized
over
the genrated type, without conflict with the base type. For example:
typegen int my_char_t;
template ctype<my_char_t> { /* ... */ };
Recent discussions in the newsgroup comp.std.c++ under the subject "Re:
ctype
virtual functions", and in particular a post by Dietmar Kuehl,
highlighted
the problem of using a built-in type (e.g. int) for a specialization of
the
ctype template: the template may or may not have been specialized by the
compiler implementation. The typegen keyword solves this.
2.4 Conversion
The type generated by a typegen has no default conversion to the base
type
nor to the generated type. But a static_cast can be used to convert
between
the two. Also, two distinct types generated by two typegen from the same
base
type have no automatic conversion between them, but a static_cast can be
used
to convert a value from one type to the other type. For example:
typegen int meter;
typegen int km;
typegen km kg;
void foo ( )
{
int i;
meter m;
km k;
kg g;
i = m; // Error: no conversion.
m = i; // Error: no conversion.
m = k; // Error: no conversion.
g = k; // Error: no conversion.
i = static_cast<int> ( m ); // OK.
m = static_cast<meter> ( i ); // OK.
m = static_cast<meter> ( k ); // OK.
g = static_cast<kg> ( k ); // OK.
}
2.5 Constants
Constant literals of the original type of a type generated using a
typegen
are converted automatically to the generated type as if a static_cast
was
used. For example, the following assignments are equivalent:
typegen int meter;
meter m1 = static_cast<meter> (3); // OK: static_cast.
meter m2 = 3; // OK: as if a static_cast was
used.
2.6 Conversion Operator
Conversion operators can also be declared between any two types,
involving at
least a generated type:
typegen int meter;
typegen int km;
operator meter ( km k ) { return static_cast<meter> (k * 1000); }
operator meter ( int i ) { return static_cast<meter> (i); }
operator int ( meter m ) { return static_cast<int> (m); }
km d_km = 2;
meter d_m = d1; // OK: conversion operator called, d_m equals 2000.
int i = d_m; // OK; conversion operator called.
int d_m = i; // OK; conversion operator called.
Note that conversion operators can also be declared for pointers and
references to generated, base, and original types.
Using conversion has two roles. First, allowing the use of many
generated
types while controlling where each can be used and the rules of
conversion.
Second, by providing conversion to the original type, it allows reuse of
functions of the original type. For example, if int is the original
type, the
function atoi() and the stream operators can be used.
Note that declaring two conversion operators for two generated types
over the
same base type won't compromise type-safety since the compile cannot
invoke
two user-declared conversion operators implicitly. For example:
typegen int meter;
typegen int km;
operator km ( int i ) { return static_cast<km> (i); }
operator int ( meter m ) { return static_cast<int> (m); }
meter m = 3; // OK.
km k = m; // Error: cannot call two user defined conversion ops.
2.7 Code Reuse
The intention of the design of the typegen is to allow full code reuse.
That
is, the intention is not that functions and object code be duplicated.
These
can be reused as-is, but the compiler keeps track of the types used and
enforce type-safety. This applies mainly to classes.
In effect, when the base type of a typegen is a class, the result is
as-if a
new class had been declared with all the members and friends, but in
which
all mentions of the base type had been replaced with the generated type.
For
example:
struct string { string(); string(const string &); friend string foo();
};
typegen string path;
// as-if: struct path { path(); path(const path &); friend path foo();
};
// had been declared.
3. Alternatives
There are several alternative ways to fulfill the need outlined in
section 1.
Each is covered in this section with their difficiencies.
3.1 Typedef
example:
typedef int meter;
typedef float pressure;
The typedef approach achieves the goal of reuse and operability. But due
to
the fact that a typedef does not introduce a new type, there is no type
safety at all among all types generated.
3.2 Enum
Using an enumeration provides type safety. The reuse is very limited
since an
enum can only be used in place of integers. Floating point are not
covered,
neither are more complex types such as character strings. Operability is
good
but all results must be cast back to the enumerated type:
enum meter { min = INT_MIN, max = INT_MAX }; // Insure integer size.
meter dist0 = 2; // Error: no conversion found.
meter dist1 = (meter) 3; // OK.
meter dist2 = (meter) 4; // OK.
meter total_1st_try = dist1 + dist2; // Error: no
conversion.
meter total_2nd_try = (meter) ( dist1 + dist2); // OK.
3.3 Class
Using a class provides much. Type safety is provided. Reuse is not.
Operability is not complete for all types. The reason that reuse is not
provided is because the original class must be derived from or it must
be
contained, both of which introduces problems. The problems with reuse
are
reviewed in the section 4.3.1. The problems with operability are
reviewed in
section 4.3.2.
3.3.1. Class Reuse Problems
This section outlines the problem with class reuse. The following
sub-sections cover each ways a class can be reused.
3.3.1.1 Public Derivation
The problem with public derivation is that type-safety is lost. Good
design
indicates that public derivation should be used only when the
child-class is
related to its parent, and that the child-class could be used everywhere
the
parent could, which is contrary to the type-safety need.
3.3.1.2 Private Derivation
The problem with private derivation is that reuse is seriously
compromised.
To achieve reuse, all public method of the parent class must be made
public
again. There are many ways to achieve this re-publication, but all
involve
knowing all the functions originaly provided. This is a protability
problem
and a maintenance problem.
3.3.1,3 Containment
The problem with containment are the same as for private derivation.
This is
normal since both are often describe as alternatives to achieve the same
goal. So they fail the same ways, not fulfilling the need for reuse.
3.3.2. Class operability problems
The first operability problem is that if private inheritance of
containment
is used, functions of the original class must be re-publicated. This
creates
maintenance problems, and requires the need to know all functions the
class
to be re-publicated.
The second problem is that to reproduce all operations of a type such as
int,
all operators must be overloaded. Forgetting one is easy and won't be
discovered until the operator is used and the compiler fails to locate
it.
Also, the type may not produce code that is optimal compared to using an
int.
While using inline functions may provide the same efficiency, it is not
possible to force the compiler to inline any function. The compiler may
decide not to inline functions.
3.4 Pre-processor
There are several ways to use the pre-processor to trick the compiler
into
generating new types. The simplest is to simply use a #define to replace
all
mention of the generated type with the original. This fails to provide
type
safety, and brings all the problem of changing a name with another with
the
pre-processor, namely that it does not respect scopes and wreak havoc
with
all code.
Another use of the pre-processor would be to change the name of the
original
type to the name of the generated type. This could be used around
#include
statements including declaration of the original type. This fails
because the
original type can be reference elsewhere. Also, to use it for a class,
it
could only work if the source code of all member functions is available.
4. Implementation
In order to verify the usefulness, applicability, and most of all,
feasability, it is necessary to provide an implementation of the typegen
keyword. I would very much like to provide a patch to the g++ compiler.
But I
am not familiar with the innards of the compiler, I cannot even begin to
assess how much time it would take me to *evaluate* how much time I
would need
to *modify* it. So this is pretty far off.
5. Critics
Criticism, errata, and pointers to prior art is very welcome. This is
the
goal of this proposition.
I already see that the section 2.7 on code reuse could be controversial.
Currently, the intent is to "bring in" implicitly only member functions
and
friend functions cand classes . All other functions are not "brought
in".
Maybe the friend keyword could be used outside of a class, on a
generated
type, which would provide for a case-by-case code reuse of non-member
functions.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: mping@scdt.intel.com (Mark Ping)
Date: 2000/03/23 Raw View
In article <38D90A32.881FB95D@artquest.net>,
Pierre Baillargeon <pb@artquest.net> wrote:
>2. Proposition
>
>To fulfill all the needs we propose a new keyword that creates a new
>type from an existing type: typegen. The syntax of the keyword is
>modeled after the typedef keyword. Every valid syntax involving the
>typedef keyword would also be valid for the typegen keyword. For
>example:
>
>typegen int meter; // OK: new type named meter generated from base
> // int.
Is there any conceived use for non-numeric types? Is this only
intended for builtin types, or for user-defined types as well?
>2.3 Template Specialization
>
>Since typegen introduces a new distinc type, template can be specialized
>over
>the genrated type, without conflict with the base type. For example:
>
> typegen int my_char_t;
>
> template ctype<my_char_t> { /* ... */ };
Since all of the examples of usage are numerical types, how will
numeric_limits<> be handled?
>3.3 Class
>
>Using a class provides much. Type safety is provided. Reuse is not.
>Operability is not complete for all types. The reason that reuse is
>not provided is because the original class must be derived from or it
>must be contained, both of which introduces problems. The problems
>with reuse are reviewed in the section 4.3.1. The problems with
>operability are reviewed in section 4.3.2.
I think a template class would provide for reuse.
In fact, if this really is for numerics only, it seems like providing
standard arithmetic operators with the template would go a long way to
solve the problem. Have you tried implementing this as a template
class?
I *do* know that unless you can argue a stronger case for a new
keyword, it's unlikely to get anywhere.
--
The above comments represent my opinion only, and in no way
reflect the opinions or policy of Intel Corp.
Mark Ping mping@scdt.intel.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]