Topic: Are literals const?
Author: virtual.larry@netzero.net (Videoman)
Date: Wed, 5 Sep 2001 09:07:29 GMT Raw View
On Wed, 22 Aug 2001 22:51:06 GMT, agriff@tin.it (Andrea Griffini)
wrote:
>On Wed, 22 Aug 2001 20:17:28 GMT, pdimov@mmltd.net (Peter Dimov)
>wrote:
>
>>What do you think about this piece of code:
>>
>>int main()
>>{
>> int const & r = 5;
>> const_cast<int &>(r) = 6;
>>}
>>
>>Is this legal? I think that it is. Literals aren't const (they are
>>about 90% const, but not 100% const.)
>
>I think that the code is legal... but not for the reason you're
>thinking. A "literal" isn't an object and isn't a value...
>so talking about the address of a "literal" or the const-ness
>of a "literal" isn't appropriate. A "literal" is something that
>doesn't belong to the world of run-time.
>
>In the code above I see a temporary non-const integer of value
>5 bound to a const reference and the standard dictates that
>this temporary has to live as long as the reference.
>
>You can even change the value using const_cast ... but you're
>just changing the value of the temporary. You never touched
>the "literal" that generated the temporary... it's not
>something you can reach with executable instructions.
>
>Actually there is some term confusion with string literals...
>those generates objects (arrays of chars) that are often
>referred to as the "literals".
Is it possible, with the current C++ standard, to be able to define
"string literals" (or arrays, at least of built-in types or PODs made
only from built-in types), that are compile-time constants, or allow
accessing their sub-object (except that these are literals, or should
be) values at compile time, if using a compile-time integral value to
index into the array?
Imagine this code:
const int const array[] = {0,1,2,3,4,5};
enum { array_size = ( sizeof(array[]) / sizeof(array[0]) ) };
template< const int INDEX_, const int TOTAL_, const int const * ARRAY_
>
class CTArraySum;
template<> template< const int TOTAL_, const int const * ARRAY_ >
class CTArraySum< 0 >
{
public: enum { value = TOTAL_ + ARRAY_[0] };
};
template< const int INDEX_, const int TOTAL_, const int const * ARRAY_
>
class CTArraySum
{
public: enum { value = TOTAL_ + CTArraySum< (INDEX_ - 1),
ARRAY_[INDEX_ - 1], ARRAY_ >::value };
};
int main(int argc, char *argv[])
{
return CTArraySum< array_size, 0, array >::value;
}
Shouldn't that program be equivalent to:
int main(int argc, char *argv[])
{
return 15;
}
Will current C++ compiliers handle something like 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.research.att.com/~austern/csc/faq.html ]
Author: pdimov@mmltd.net (Peter Dimov)
Date: Mon, 27 Aug 2001 18:49:26 GMT Raw View
joerg.barfurth@attglobal.net (Joerg Barfurth) wrote in message news:<1eykhdu.1cqeruv1s5f35sN%joerg.barfurth@attglobal.net>...
> Peter Dimov <pdimov@mmltd.net> wrote:
>
> > What do you think about this piece of code:
> >
> > int main()
> > {
> > int const & r = 5;
> > const cast<int &>(r) = 6;
> > }
> >
> > Is this legal? I think that it is. Literals aren't const (they are
> > about 90% const, but not 100% const.)
>
> IMO this is not legal.
>
> The literal is not const, but it is an rvalue. (Note: rvalues of builtin
> types are never const.) According to 8.5.3/5 (last bullet) a temporary
> of type 'const int' is created and the reference is bound to this
> temporary.
Yes, you are right. This is actually a good thing. It means that this
code is not legal, and hence, doesn't count in the category "legal
code that breaks when rvalues of built-in types are made 100% const."
> > Do we really need non-const literals and rvalues (of built-in
> > types)?
>
> I don't think it matters (afaict you can't exploit the non-constness).
It does.
template<class T> void h(T & t) {}
int main()
{
h(5); // fail
}
--
Peter Dimov
Multi Media Ltd.
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Fri, 24 Aug 2001 16:33:29 GMT Raw View
joerg.barfurth@attglobal.net (Joerg Barfurth) wrote (abridged):
> As I argued in another post: This is described in 8.5.3/5 (last bullet).
> The temporary has type 'const int' and thus the code is not legal.
OK, you've convinced me. In that situation the temporary gets its type
from the type of the reference, not the type of the expression.
> Now, if you had
>
> Complex const & r = Complex(1);
>
> the initializer is an rvalue of type Complex (non-const).
>
> In this case the next-to-last paragraph of 8.5.3/5 applies. This allows
> binding the reference to that rvalue directly. But it also allows
> copying to another temporary,which would have type 'const Complex'.
Did that change since the draft? The draft seems to say:
A temporary of type "cv1 T2" [sic] is created...
where T2 is the type of the expression, not the reference. So we either
bind the reference to the non-const rvalue, or we create a non-const
temporary and bind to that. Either way the reference is bound to a
non-const object.
The difference between a const lvalue and a non-const rvalue has come up
in comp.lang.c++.moderated. I can't say I'm very comfortable with it.
Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
brangdon@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: Fri, 24 Aug 2001 17:20:58 GMT Raw View
<thp@cs.ucr.edu> wrote:
> Until recently I though that only lvalues could be const, but rvalues
> of struct/class types have a this-pointer by which their member
> functions can modify them. So:
>=20
> struct Stuff {
> void bar() const;
> void baz();
> };
>=20
> const Stuff foo();
>=20
> int main() {
> foo().bar(); // cool
> foo().baz(); // error -- violates constness of rvalue foo().
> }
>=20
> That wedge opens the door for both const and volatile rvalues of all
> types. (I'm still getting used to the notion.)
No. It opens the door only for const and volatile rvalues of class type.
rvalues of non-class types are not cv-qualified.
Regards, J=F6rg
--=20
J=F6rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Fri, 24 Aug 2001 20:46:52 GMT Raw View
In article <1eylyrg.gd69zat6jhigN%joerg.barfurth@attglobal.net>, Joerg
Barfurth <joerg.barfurth@attglobal.net> writes
>This remains true, even though you can't modify such an rvalue. This
>fact isn't true because of the type, but because such an rvalue is a
>value - not an object.
bool foo(int &){ return false;}
bool foo(int const &){return true;}
int main(){
foo(5);
}
How do we want this code to behave? If the type of 'f' was const int
there would be no problem, instead we, in these cases, distinguish
between lvalues and rvalues and declare '5' to be an rvalue. While
understanding the reasoning, I think that it causes problems.
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: pdimov@mmltd.net (Peter Dimov)
Date: Wed, 22 Aug 2001 20:17:28 GMT Raw View
What do you think about this piece of code:
int main()
{
int const & r = 5;
const_cast<int &>(r) = 6;
}
Is this legal? I think that it is. Literals aren't const (they are
about 90% const, but not 100% const.)
Similarly:
int f()
{
return 5;
}
int main()
{
int const & r = f();
const_cast<int &>(r) = 6;
}
Do we _really_ need non-const literals and rvalues (of built-in
types)?
--
Peter Dimov
Multi Media Ltd.
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Wed, 22 Aug 2001 21:49:37 GMT Raw View
pdimov@mmltd.net (Peter Dimov) wrote (abridged):
> What do you think about this piece of code:
>
> int main()
> {
> int const & r = 5;
> const_cast<int &>(r) = 6;
> }
>
> Is this legal? I think that it is.
I think it is too. The first line is, the second line is and I don't see
any undefined behaviour. The type of 5 is (int), not (const int).
> Literals aren't const (they are about 90% const, but not 100% const.)
I think the reference is bound to a temporary, not a literal. I don't
think r is a compile-time constant (just as well given we can assign to
it). Eg:
int const &r = 1;
switch (j) {
case r: // Error - not a constant.
}
However, I don't know when and how it stops being a literal.
> Do we _really_ need non-const literals and rvalues (of built-in
> types)?
Yes. I'd prefer to minimise the differences between built-in types and
class types. Eg:
class Complex {
public:
Complex( int i, int j=0 );
//...
};
Complex const &r = 1;
const_cast<Complex &>(r) = 2;
This is more or less reasonable for class types, so it should be permitted
for built-in types too, for the sake of templates:
template <typename T>
void demo() {
T const &r = 1;
const_cast<T &>( r ) = 2;
}
Unless there is a strong reason to have a difference. Is there?
Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
brangdon@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: agriff@tin.it (Andrea Griffini)
Date: Wed, 22 Aug 2001 22:51:06 GMT Raw View
On Wed, 22 Aug 2001 20:17:28 GMT, pdimov@mmltd.net (Peter Dimov)
wrote:
>What do you think about this piece of code:
>
>int main()
>{
> int const & r = 5;
> const_cast<int &>(r) = 6;
>}
>
>Is this legal? I think that it is. Literals aren't const (they are
>about 90% const, but not 100% const.)
I think that the code is legal... but not for the reason you're
thinking. A "literal" isn't an object and isn't a value...
so talking about the address of a "literal" or the const-ness
of a "literal" isn't appropriate. A "literal" is something that
doesn't belong to the world of run-time.
In the code above I see a temporary non-const integer of value
5 bound to a const reference and the standard dictates that
this temporary has to live as long as the reference.
You can even change the value using const_cast ... but you're
just changing the value of the temporary. You never touched
the "literal" that generated the temporary... it's not
something you can reach with executable instructions.
Actually there is some term confusion with string literals...
those generates objects (arrays of chars) that are often
referred to as the "literals".
In my opinion the two (what you typed in the source and what
you get the address of at run-time) are two disinct entities.
For example when I'm talking of escape sequences in literals
I'm referring to literals in the source but when I'm talking
about convertibility of literals to regular non-const char *
(for C compatibility) then I'm talking about the address
of "string literal objects".
Just my opinion, of course
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: clarkcox3@yahoo.com (Clark S. Cox III)
Date: Wed, 22 Aug 2001 23:46:25 GMT Raw View
Peter Dimov <pdimov@mmltd.net> wrote:
> What do you think about this piece of code:
>
> int main()
> {
> int const & r = 5;
> const_cast<int &>(r) = 6;
> }
>
> Is this legal? I think that it is. Literals aren't const (they are
> about 90% const, but not 100% const.)
Literals aren't objects. In the code above, a temporary object is
created with the value 5, a reference is then made to that temporary
object, and 6 is assigned to that temporary object.
--
Clark S. Cox III
clarkcox3@yahoo.com
http://www.whereismyhead.com/clark/
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: Thu, 23 Aug 2001 00:01:23 GMT Raw View
Dave Harris <brangdon@cix.co.uk> wrote:
> pdimov@mmltd.net (Peter Dimov) wrote (abridged):
> > What do you think about this piece of code:
> >=20
> > int main()
> > {
> > int const & r =3D 5;
> > const_cast<int &>(r) =3D 6;
> > }
> >=20
> > Is this legal? I think that it is.
>=20
> I think it is too. The first line is, the second line is and I don't se=
e
> any undefined behaviour. The type of 5 is (int), not (const int).
>=20
> > Literals aren't const (they are about 90% const, but not 100% const.)
>=20
> I think the reference is bound to a temporary, not a literal.=20
> However, I don't know when and how it stops being a literal.
As I argued in another post: This is described in 8.5.3/5 (last bullet).
The temporary has type 'const int' and thus the code is not legal.
> > Do we _really_ need non-const literals and rvalues (of built-in
> > types)?
>=20
> Yes. I'd prefer to minimise the differences between built-in types and
> class types.=20
The present system is pretty close at that (in this regard).
> Eg:
>=20
> class Complex {
> public:
> Complex( int i, int j=3D0 );
> //...
> };
>=20
> Complex const &r =3D 1;
> const_cast<Complex &>(r) =3D 2;
>=20
> This is more or less reasonable for class types, [...]
But it is illegal as well. The same bullet of 8.5.3/5 applies.
Now, if you had=20
Complex const & r =3D Complex(1);
the initializer is an rvalue of type Complex (non-const).=20
In this case the next-to-last paragraph of 8.5.3/5 applies. This allows
binding the reference to that rvalue directly. But it also allows
copying to another temporary,which would have type 'const Complex'.
This means that the code for most means is still illegal. I wonder
though, why the choice is 'implementation-defined' here rather than
'unspecified'. This means, that if an implementation documents that it
always uses the direct way of binding here, the code would be legal for
that implementation.
BTW: What choice is documented (or is it undocumented ?) for existing
compilers ?=20
(Sorry, I have no compiler here, so I can't start the list).
> [...] so it should be permitted=20
> for built-in types too, for the sake of templates:
> Unless there is a strong reason to have a difference. Is there?
Which now means it shouldn't ...
Regards, J=F6rg
--=20
J=F6rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: Thu, 23 Aug 2001 00:14:33 GMT Raw View
Peter Dimov <pdimov@mmltd.net> wrote:
> What do you think about this piece of code:
>=20
> int main()
> {
> int const & r =3D 5;
> const_cast<int &>(r) =3D 6;
> }
>=20
> Is this legal? I think that it is. Literals aren't const (they are
> about 90% const, but not 100% const.)
IMO this is not legal.=20
The literal is not const, but it is an rvalue. (Note: rvalues of builtin
types are never const.) According to 8.5.3/5 (last bullet) a temporary
of type 'const int' is created and the reference is bound to this
temporary.
As the temporary is const (100% :-o), the const_cast + modification
invokes undefined behavior.
> Similarly:
>=20
> int f();
> int main()
> {
> int const & r =3D f();
> const_cast<int &>(r) =3D 6;
> }
>=20
> Do we _really_ need non-const literals and rvalues (of built-in
> types)?
I don't think it matters (afaict you can't exploit the non-constness).
BTW: The only literals that are lvalues are string literals (5.1/2).
Those are const to begin with.
Regards, Joerg
--=20
J=F6rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: comeau@panix.com (Greg Comeau)
Date: Thu, 23 Aug 2001 09:22:38 GMT Raw View
In article <7dc3b1ea.0108220617.421ee131@posting.google.com>,
Peter Dimov <pdimov@mmltd.net> wrote:
> int const & r = 5;
> const_cast<int &>(r) = 6;
>
>Is this legal? I think that it is.
>Literals aren't const (they are
>about 90% const, but not 100% const.)
I don't think that's the issue. The issue is which qualifiers it
"picks up." This is really your code:
const int temp = 5;
int const & r = temp;
const_cast<int &>(r) = 6;
Do you think that's ok?
--
Greg Comeau Countdown to "export": December 1, 2001
Comeau C/C++ ONLINE ==> http://www.comeaucomputing.com/tryitout
World Class Compilers: Breathtaking C++, Amazing C99, Fabulous C90.
Comeau C/C++ with Dinkumware's Libraries... Have you tried it?
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Nicola Musatti <objectway@divalsim.it>
Date: Thu, 23 Aug 2001 09:23:21 GMT Raw View
Joerg Barfurth wrote:
[...]
> (Note: rvalues of builtin types are never const.)
Isn't it rather the reverse? rvalues of builtin types aren't modifiable;
moreover, the only way to *directly* modify an rvalue is to call a non
const member function on it.
Best regards,
Nicola Musatti
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: thp@cs.ucr.edu
Date: Thu, 23 Aug 2001 17:48:17 GMT Raw View
Peter Dimov <pdimov@mmltd.net> wrote:
: What do you think about this piece of code:
: int main()
: {
: int const & r = 5;
: const_cast<int &>(r) = 6;
: }
: Is this legal? I think that it is. Literals aren't const (they are
: about 90% const, but not 100% const.)
: Similarly:
: int f()
: {
: return 5;
: }
: int main()
: {
: int const & r = f();
: const_cast<int &>(r) = 6;
: }
AFIK, they are both legal, and so is:
int main()
{
int x = 5;
int const & r = x;
const_cast<int &>(r) = 6;
}
IIRC, a const reference can refer to a non-const object but can't be
used to modify it.
: Do we _really_ need non-const literals and rvalues (of built-in
: types)?
Until recently I though that only lvalues could be const, but rvalues
of struct/class types have a this-pointer by which their member
functions can modify them. So:
struct Stuff {
void bar() const;
void baz();
};
const Stuff foo();
int main() {
foo().bar(); // cool
foo().baz(); // error -- violates constness of rvalue foo().
}
That wedge opens the door for both const and volatile rvalues of all
types. (I'm still getting used to the notion.)
Tom Payne
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: Thu, 23 Aug 2001 21:33:24 GMT Raw View
On Wed, 22 Aug 2001 20:17:28 GMT, pdimov@mmltd.net (Peter Dimov) wrote:
> What do you think about this piece of code:
>
> int main()
> {
> int const & r = 5;
> const_cast<int &>(r) = 6;
> }
>
> Is this legal? I think that it is. Literals aren't const (they are
> about 90% const, but not 100% const.)
Sure, its "legal", with undefined behaviour.
> Similarly:
>
> int f()
> {
> return 5;
> }
>
> int main()
> {
> int const & r = f();
> const_cast<int &>(r) = 6;
> }
Same here.
Read the fine print on binding a const& to an rvalue. In the case of
built-ins, a temporary is required. The temporary may be of const
type and removing const from a const object gives undefined behaviour.
The undefined behaviour usually does what you expect because the temp
is not really in read only memory. Playing with fire is fun. ;-)
John
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: Thu, 23 Aug 2001 22:55:57 GMT Raw View
Nicola Musatti <objectway@divalsim.it> wrote:
> Joerg Barfurth wrote:
> [...]
> > (Note: rvalues of builtin types are never const.)
>=20
> Isn't it rather the reverse? rvalues of builtin types aren't modifiable=
;
> moreover, the only way to *directly* modify an rvalue is to call a non
> const member function on it.
According to 3.10/9 rvalues of non-class types have cv-unqualified
types. You might say that the categories of constness and volatileness
don't apply to them. IOW within the framework of the C++ type systems
the type of an int rvalue is always 'int' which is not const (whereas
the type of an rvalue of class type may be cv-qualified.
This remains true, even though you can't modify such an rvalue. This
fact isn't true because of the type, but because such an rvalue is a
value - not an object.
Regards, J=F6rg
--=20
J=F6rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ 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.research.att.com/~austern/csc/faq.html ]