Topic: Unions up in arms!


Author: glenlow@pixelglow.com (Glen Low)
Date: Wed, 15 Sep 2004 14:34:18 +0000 (UTC)
Raw View
> The wording is a bit difficult, to put it mildly.  I think that the k
> ey
> is in the (non-normative) footnote.  These restrictions are basicly
> there to tell compiler writers what they can exclude (or rather what
> they cannot exclude) as possible aliases.  Thus, if I have
>
>     strut S { int i ; float f ; } ;
>     float ff ;
>
>     S* ps ;
>     float* pf ;
>
> The compiler must assume that *pf is also accessible via ps.

Hewing close to the language of the Standard, if I store into *pf, the
compiler must assume that a read from *ps would be affected. The
question is, are the following reads also affected: ps->i, ps->f ?
Note that *ps has an lvalue of union type and so the above analysis is
correct, but how about ps->f which has an lvalue of float type? Does
"through an lvalue" mean "an lvalue expression of this exact type" or
"an lvalue expression that contains this type in it e.g. as part of a
member access"?

For the sake of argument, let's assume that sizeof (int) == sizeof
(float) and the data in question is a valid sequence of bits for
either int or float.

Cheers,

Glen Low, Pixelglow Software
www.pixelglow.com


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: llewelly <llewelly.at@xmission.dot.com>
Date: Wed, 15 Sep 2004 20:19:20 +0000 (UTC)
Raw View
glenlow@pixelglow.com (Glen Low) writes:

>  > Aren't you making a fairly big assumption?
>  > What if sizeof(int) != sizeof(float)?
>
> Let's assume that it is so.
>
>  > >And finally, what does "including, recursively, a member of a
>  > >subaggregate or contained union" mean?
>
> Well, interpretation A was that:
>
> an object of type int can be accessed by a lvalue of type union { int
> i; float f }; and also an lvalue of type union { union { int i; float
> f}; float g; };
>
> But that doesn't make sense, since if the lvalue is constrained to be
> of struct type instead, you could possibly access an undefined value
> e.g. the float value.
>
> Interpretation B is that
>
> an object of type int can be accessed by a lvalue of type union { int
> i; float f; }.i or union {int i; float f; }.f etc.
>
> This hinges on the phrase "through an lvalue", since the actual lvalue
> is no longer of union (or struct) type. I see a lot of gcc users
> advocating this, e.g. Mark Mitchell of Codesourcery who contributed
> the type-based alias analysis code to gcc.
[snip]

I think if asked him whether or not it was conforming, he would tell
    you 'no'.

Wherever I've seen him 'advocating' that construct, it is to replace
    an older form of non-standard type punning which gcc no longer
    supports, with a different form of non-standard type punning
    which gcc still supports; it was always clear from context that
    such constructs were not conforming.

IOWs, when compiled with gcc, it will do what experienced gcc users
    expect, but it is not conforming.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: swelef@post.sk (Vladimir Marko)
Date: Fri, 17 Sep 2004 06:14:59 GMT
Raw View
tannhauser86549spam@free.fr (Falk Tannh   user) wrote in message news:<41476579$0$12975$626a14ce@news.free.fr>...
> Vladimir Marko wrote:
>
> > kanze@gabi-soft.fr wrote in message news:<d6652001.0409130421.2c2cccc1@
>  posting.google.com>...
> >
> >>    struct S { int i ; float f ; } ;
> >>    float ff ;
> >>
> >>    S* ps ;
> >>    float* pf ;
>  [...]
> >>For the rest, there are a lot of other rules which have to be taken
> >>into account.  Thus, for example, *(float*)ps is definitly not
> >>a legal access, despite the text you quote.
> >
> > Oh, really? First, we have 9.2/17:
> >   A pointer to a POD-struct object, suitably converted using a
> >   reinterpret cast, points to its initial member (or if that member
> >   is a bit-field, then to the unit in which it resides) and vice versa.
> > Thus, *(int*)ps is surely valid.
>
> OK. It's an lvalue referring to the same object as 'ps->i'.
>
> > Then, 9.5/1 says
> >   [snip] Each data member is allocated as if it were the sole member of
> >   a struct. [snip]
>
> But 'ps' in the above example is a pointer to struct, and   9.5/1
> concerns unions.

I appologize. The thread is about unions and I misread the Kanze's
example as if there was a union too. (And I also forgot to mention
that 9.5 is about unions.)

> If 'S' were a union, I agree that 'reinterpret cast<float*>(ps)'
> would be well defined and equal to '&ps->f'.
>
> > It comes down to the question "How deep is that 'as if'?" Is it deep
> > enough for the 9.2/17 to apply?
>
> Well, being "the sole member of a struct" implies being its initial
> member, so   9.2/17 indirectly applies to each union member IMO.

The wording is "allocated as if". This suggests that it only applies
to the memory layout and 9.2/17 is about reinterpret_cast validity
not about memory layout. It definitely can't apply to the paragraph
8.5.1/15 [dlc.init.aggr/unions]:
  When a union is initialized with a brace-enclosed initializer,
  the braces shall only contain an initializer for the first member
  of the union.
And what's the difference between "initial member" (in 9.2/17 and
"first member" (in 8.5.1/15)?

I'll just repeat myself by writing that I think that the standard
says "undefined behaviour" while each reasonable implementation
makes it well defined.

Vladimir Marko

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Fri, 17 Sep 2004 15:29:56 +0000 (UTC)
Raw View
glenlow@pixelglow.com (Glen Low) wrote in message
news:<9215d7ac.0409140039.727afd2f@posting.google.com>...

> > The wording is a bit difficult, to put it mildly. I think that the
> > key is in the (non-normative) footnote. These restrictions are
> > basicly there to tell compiler writers what they can exclude (or
> > rather what they cannot exclude) as possible aliases. Thus, if I
> > have

> >     strut S { int i ; float f ; } ;
> >     float ff ;
> >
> >     S* ps ;
> >     float* pf ;

> > The compiler must assume that *pf is also accessible via ps.

> Hewing close to the language of the Standard, if I store into *pf, the
> compiler must assume that a read from *ps would be affected.

Correct.

> The question is, are the following reads also affected: ps->i, ps->f ?

Good question.  ps->f is definitly affected.  Formally, I don't think
that the compiler is obliged to take into account ps->i -- reading from
ps->i is undefined behavior if you've written to ps->f, and if *pf
wasn't an alias for ps->f, ps->i shouldn't change.

In practice, I would consider it a poor compiler which didn't support
this.  Correct or not, I think it is a common idiom.  And although you
do loose an optimization possibility, you only loose it in the presence
of a union, which shouldn't be that frequent.  (FWIW: at least one early
C compiler I used, Microsoft C 1.0, didn't support it -- writing into
one field of a union, and reading from another often didn't work.)

> Note that *ps has an lvalue of union type and so the above analysis is
> correct, but how about ps->f which has an lvalue of float type? Does
> "through an lvalue" mean "an lvalue expression of this exact type" or
> "an lvalue expression that contains this type in it e.g. as part of a
> member access"?

> For the sake of argument, let's assume that sizeof (int) == sizeof
> (float) and the data in question is a valid sequence of bits for
> either int or float.

Both of which are irrelevant with regards to the current discussion.
The standard says that reading from  any element other than the last one
written is undefined behavior.  It doesn't say why, and the compiler is
free to assume that your code doesn't do it.

--
James Kanze           GABI Software         http://www.gabi-soft.fr
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Peter Koch Larsen <pklspam@mailme.dk>
Date: Mon, 13 Sep 2004 15:37:25 +0000 (UTC)
Raw View

"Glen Low" <glenlow@pixelglow.com> skrev i en meddelelse
news:9215d7ac.0409081832.4ab088d6@posting.google.com...
>
> Color me stupid, but after months of staring I still can't grok this
> clause in the Standard 3.10/15:
>
> ----
>
> If a program attempts to access the stored value of an object through
> an lvalue of other than one of the following types the behavior is
> undefined:
>
> an aggregate or union type that includes one of the aforementioned
> types among its members (including, recursively, a member of a
> subaggregate or contained union),
>
> ----
>
> Suppose
>
> int j;
> union u { int i; float f; };
>
> Then which of the following is illegal, if any, and why? (Putting
> aside, if possible, the legality of the popular type-punning practice
> of putting x into a union and getting back y.)

None of them are illegal, but they all give undefined behaviour. In effect,
this means that if it works in your environment it is okay as your code is
nonportable anyway (relying on int and float having same size and alignment
requirements).
>
> A.
>
> j = 12;
> int k = ((u&) j).i;
>
> B.
>
> j = 12;
> u v = (u&) j;
>
> C.
>
> u v = {12};
> float g = v.f;
>
> D.
>
> j = 12;
> float g = ((u&) j).f;
>
> And finally, what does "including, recursively, a member of a
> subaggregate or contained union" mean?
>
Sorry - no idea... are you quoting the standard?

[snip]

/Peter


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glenlow@pixelglow.com (Glen Low)
Date: Mon, 13 Sep 2004 15:37:26 +0000 (UTC)
Raw View
 > Aren't you making a fairly big assumption?
 > What if sizeof(int) != sizeof(float)?

Let's assume that it is so.

 > >And finally, what does "including, recursively, a member of a
 > >subaggregate or contained union" mean?

Well, interpretation A was that:

an object of type int can be accessed by a lvalue of type union { int
i; float f }; and also an lvalue of type union { union { int i; float
f}; float g; };

But that doesn't make sense, since if the lvalue is constrained to be
of struct type instead, you could possibly access an undefined value
e.g. the float value.

Interpretation B is that

an object of type int can be accessed by a lvalue of type union { int
i; float f; }.i or union {int i; float f; }.f etc.

This hinges on the phrase "through an lvalue", since the actual lvalue
is no longer of union (or struct) type. I see a lot of gcc users
advocating this, e.g. Mark Mitchell of Codesourcery who contributed
the type-based alias analysis code to gcc.

Interpretation C is that

an object of type union { int i; float f; }; or union { union { int i;
float f; } float g; }; can be accessed by a lvalue of type int or
float

This seems the safest, but relies on twisting the order of the
language in the Standard.

So which is it?

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Mon, 13 Sep 2004 22:47:09 +0000 (UTC)
Raw View
glenlow@pixelglow.com (Glen Low) wrote in message
news:<9215d7ac.0409081832.4ab088d6@posting.google.com>...

> Color me stupid, but after months of staring I still can't grok thi=
s
> clause in the Standard 3.10/15:

> ----

> If a program attempts to access the stored value of an object throu=
gh
> an lvalue of other than one of the following types the behavior is
> undefined:

> an aggregate or union type that includes one of the aforementioned
> types among its members (including, recursively, a member of a
> subaggregate or contained union),

> ----

The wording is a bit difficult, to put it mildly.  I think that the k=
ey
is in the (non-normative) footnote.  These restrictions are basicly
there to tell compiler writers what they can exclude (or rather what
they cannot exclude) as possible aliases.  Thus, if I have

    strut S { int i ; float f ; } ;
    float ff ;

    S* ps ;
    float* pf ;

The compiler must assume that *pf is also accessible via ps.

For the rest, there are a lot of other rules which have to be taken i=
nto
account.  Thus, for example, *(float*)ps is definitly not a legal
access, despite the text you quote.

> Suppose

> int j;
> union u { int i; float f; };

> Then which of the following is illegal, if any, and why? (Putting
> aside, if possible, the legality of the popular type-punning practi=
ce
> of putting x into a union and getting back y.)

But you can't put it aside.  The standard clearly says that the only
member of a union that can be accessed is the last one written.

> A.

> j =3D 12;
> int k =3D ((u&) j).i;

This is a reinterpret_cast.  According to =A75.2.10/7, "Except that
converting an rvalue of type 'pointer to T1' to the type 'pointer to
T2' (where T1 and T2 are object types and where the alignement
requirements of T2 are no stricter than those of T1) and back to its
original type yields the original pointer value, the result of such a
pointer conversion is unspecified."  So there results here are
unspecified.

Note that even if you converted back to the original type, if float
required stricter alignment (or if the compiler imposed stricter
alignment on unions than on its constituant members), the results wou=
ld
be unspecified.

> B.

> j =3D 12;
> u v =3D (u&) j;

Unspecified, see above.

> C.

> u v =3D {12};
> float g =3D v.f;

The union was initialized with an int.  You access the float field.
Undefined behavior.  On machines using IEEE, this can cause a trappin=
g
NaN.  On machines where floats are larger than ints, who knows what m=
ay
happen.  In this case, it doesn't even work in practice.

> D.

> j =3D 12;
> float g =3D ((u&) j).f;

You're using an unspecified pointer to access a field in a union that
doesn't exist.  What do you expect to happen.

> And finally, what does "including, recursively, a member of a
> subaggregate or contained union" mean?

It means that if you have a float*, and a U*, for some union or
aggregate, if that union or aggregate contains a float, directly, or
indirectly, as a member of one of its members, then the compiler must
consider that the the two pointers may possibly be aliases for the sa=
me
data, i.e. if there is a write through one of the pointers, the compi=
ler
must reread any value read through the other, since this write may ha=
ve
modified the value.

--
James Kanze           GABI Software         http://www.gabi-soft.fr
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 =
34


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting wi=
th ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu =
   ]
[              --- Please see the FAQ before posting. ---            =
   ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                    =
   ]





Author: swelef@post.sk (Vladimir Marko)
Date: Tue, 14 Sep 2004 17:37:18 GMT
Raw View
kanze@gabi-soft.fr wrote in message news:<d6652001.0409130421.2c2cccc1@posting.google.com>...
> glenlow@pixelglow.com (Glen Low) wrote in message
> news:<9215d7ac.0409081832.4ab088d6@posting.google.com>...
[snip]
> Thus, if I have
>
>     strut S { int i ; float f ; } ;
>     float ff ;
>
>     S* ps ;
>     float* pf ;
>
> The compiler must assume that *pf is also accessible via ps.
>
> For the rest, there are a lot of other rules which have to be taken
> into account.  Thus, for example, *(float*)ps is definitly not
> a legal access, despite the text you quote.

Oh, really? First, we have 9.2/17:
  A pointer to a POD-struct object, suitably converted using a
  reinterpret_cast, points to its initial member (or if that member
  is a bit-field, then to the unit in which it resides) and vice versa.
Thus, *(int*)ps is surely valid. Then, 9.5/1 says
  [snip] Each data member is allocated as if it were the sole member of
  a struct. [snip]
It comes down to the question "How deep is that 'as if'?" Is it deep
enough for the 9.2/17 to apply? Given the exact wording, I'd say no
(i.e. you are right). On the other hand, making it behave wrong would
require a special handling of this case in the implementation's
reinterpret_cast. AFAICT the standard could be changed to allow it
without any implications for the vendors.

> > Suppose
>
> > int j;
> > union u { int i; float f; };
>
> > Then which of the following is illegal, if any, and why? (Putting
> > aside, if possible, the legality of the popular type-punning practi
>  ce
> > of putting x into a union and getting back y.)
>
> But you can't put it aside.  The standard clearly says that the only
> member of a union that can be accessed is the last one written.
>
> > A.
>
> > j = 12;
> > int k = ((u&) j).i;
>
> This is a reinterpret cast.  According to  5.2.10/7, "Except that
> converting an rvalue of type 'pointer to T1' to the type 'pointer to
> T2' (where T1 and T2 are object types and where the alignement
> requirements of T2 are no stricter than those of T1) and back to its
> original type yields the original pointer value, the result of such a
> pointer conversion is unspecified."  So there results here are
> unspecified.
>
> Note that even if you converted back to the original type, if float
> required stricter alignment (or if the compiler imposed stricter
> alignment on unions than on its constituant members), the results wou
> ld
> be unspecified.

But _if the union has no stricter alignment requirements_, the
following is well defined:
  u* pu=(u*)&j;
  int k=*(int*)pu;
And in the light of 9.2/17, the last line is pretty much equivalent to
  int k=pu->i;
Casting references instead of pointers gives the same behaviour,
thus the original example could be valid _if the union had no stricter
alignment requirements_, couldn't it? Well, "pretty much equivalent"
is not the same as "equivalent." 9.2/17 speaks about converting
a pointer to POD struct and pu is _not_ a pointer to an POD struct,
it is just a correctly typed pointer "somewhere." So, it's again
something that should work with a reasonable implementation while
beeing marked as undefined behaviour by the standard. This time,
however, even if the standard could be changed to accept this, it
should _not_ be changed.

Vladimir Marko

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: tannhauser86549spam@free.fr (=?ISO-8859-1?Q?Falk_Tannh=E4user?=)
Date: Tue, 14 Sep 2004 22:59:42 GMT
Raw View
Vladimir Marko wrote:

> kanze@gabi-soft.fr wrote in message news:<d6652001.0409130421.2c2cccc1@=
posting.google.com>...
>=20
>>    struct S { int i ; float f ; } ;
>>    float ff ;
>>
>>    S* ps ;
>>    float* pf ;
[...]
>>For the rest, there are a lot of other rules which have to be taken
>>into account.  Thus, for example, *(float*)ps is definitly not
>>a legal access, despite the text you quote.
>=20
> Oh, really? First, we have 9.2/17:
>   A pointer to a POD-struct object, suitably converted using a
>   reinterpret_cast, points to its initial member (or if that member
>   is a bit-field, then to the unit in which it resides) and vice versa.
> Thus, *(int*)ps is surely valid.

OK. It's an lvalue referring to the same object as 'ps->i'.

> Then, 9.5/1 says
>   [snip] Each data member is allocated as if it were the sole member of
>   a struct. [snip]

But 'ps' in the above example is a pointer to struct, and =A7 9.5/1 conce=
rns
unions. If 'S' were a union, I agree that 'reinterpret_cast<float*>(ps)'
would be well defined and equal to '&ps->f'.

> It comes down to the question "How deep is that 'as if'?" Is it deep
> enough for the 9.2/17 to apply?

Well, being "the sole member of a struct" implies being its initial
member, so =A7 9.2/17 indirectly applies to each union member IMO.

Falk Tannh=E4user

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Bob Hairgrove <invalid@bigfoot.com>
Date: Sat, 11 Sep 2004 15:30:44 +0000 (UTC)
Raw View
On Fri, 10 Sep 2004 15:13:17 +0000 (UTC), glenlow@pixelglow.com (Glen
Low) wrote:

>
>Color me stupid, but after months of staring I still can't grok this
>clause in the Standard 3.10/15:
>
>----
>
>If a program attempts to access the stored value of an object through
>an lvalue of other than one of the following types the behavior is
>undefined:
>
>an aggregate or union type that includes one of the aforementioned
>types among its members (including, recursively, a member of a
>subaggregate or contained union),
>
>----
>
>Suppose
>
>int j;
>union u { int i; float f; };
>
>Then which of the following is illegal, if any, and why? (Putting
>aside, if possible, the legality of the popular type-punning practice
>of putting x into a union and getting back y.)
>
>A.
>
>j = 12;
>int k = ((u&) j).i;
>
>B.
>
>j = 12;
>u v = (u&) j;
>
>C.
>
>u v = {12};
>float g = v.f;
>
>D.
>
>j = 12;
>float g = ((u&) j).f;

Aren't you making a fairly big assumption?
What if sizeof(int) != sizeof(float)?

>And finally, what does "including, recursively, a member of a
>subaggregate or contained union" mean?

Just what it says.

--
Bob Hairgrove
NoSpamPlease@Home.com


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]






Author: glenlow@pixelglow.com (Glen Low)
Date: Fri, 10 Sep 2004 15:13:17 +0000 (UTC)
Raw View
Color me stupid, but after months of staring I still can't grok this
clause in the Standard 3.10/15:

----

If a program attempts to access the stored value of an object through
an lvalue of other than one of the following types the behavior is
undefined:

an aggregate or union type that includes one of the aforementioned
types among its members (including, recursively, a member of a
subaggregate or contained union),

----

Suppose

int j;
union u { int i; float f; };

Then which of the following is illegal, if any, and why? (Putting
aside, if possible, the legality of the popular type-punning practice
of putting x into a union and getting back y.)

A.

j = 12;
int k = ((u&) j).i;

B.

j = 12;
u v = (u&) j;

C.

u v = {12};
float g = v.f;

D.

j = 12;
float g = ((u&) j).f;

And finally, what does "including, recursively, a member of a
subaggregate or contained union" mean?

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]