Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why operator<<(bool) for volatile char* ??

5 views
Skip to first unread message

Paolo Carlini

unread,
Aug 21, 2004, 12:07:24 PM8/21/04
to
Hi,

anyone is willing to explain why this snippet

volatile char str[] = "a";
std::cout << str;

prints "1"? Or, in other terms, why operator<<(bool) is
the best match?

Thanks in advance,
Paolo.

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

Ulrich Eckhardt

unread,
Aug 22, 2004, 6:58:41 PM8/22/04
to
Paolo Carlini wrote:
> anyone is willing to explain why this snippet
>
> volatile char str[] = "a";
> std::cout << str;
>
> prints "1"? Or, in other terms, why operator<<(bool) is
> the best match?

When passed to a function, 'str' decays to a 'char volatile*'. Looking at
all overloads of 'operator<<', there is none matching it. 'void const*' or
'char const*' come close, but only that. Therefore, the best way it to
evaluate the pointer as a boolean expression, i.e. 'str!=0' and that
yields 'true' or '1'.
In case you wonder why 'char const*' does not fit, the reason is that
'volatile' (in that context) works just like 'const': it can be added but
not removed implicitly. To remove the 'volatile' qualifier, you need a
'const_cast'.

Uli

--
Questions ?
see C++-FAQ Lite: http://parashift.com/c++-faq-lite/ first !

Daryle Walker

unread,
Aug 22, 2004, 7:07:51 PM8/22/04
to
In article <2oomqsF...@uni-berlin.de>,
Paolo Carlini <pcar...@suse.de> wrote:

> anyone is willing to explain why this snippet
>
> volatile char str[] = "a";
> std::cout << str;
>
> prints "1"? Or, in other terms, why operator<<(bool) is
> the best match?

I think the "volatile" is screwing you up.

First, since there are (almost) no array operations, "str" devolves to a
pointer. So we want the closest match to one of:

1. std::ostream & std::ostream::operator << ( char volatile * );
2. std::ostream & operator <<( std::ostream &, char volatile * );

([1] is a member function, [2] is a free function.)

We have these member/free functions:

3. std::ostream & std::ostream::operator << ( void const * );
4. std::ostream & operator <<( std::ostream &, char const * );

What you're missing is that:
"T *" -> "T const *" is implicit
"T *" -> "T volatile *" is also implicit
But
"T volatile *" -> "T const *" can't be done implicitly! An explicit
const_cast is needed, so [3] and [4] are _not_ considered.

This means that the "bool" conversion is the only valid one. (We
wouldn't have this problem if the pointer inserters used "char|void
const volatile *" instead.)

This problem will get worse if we add C-1999's "restrict".

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

Paolo Carlini

unread,
Aug 23, 2004, 6:24:53 AM8/23/04
to
Thanks for your reply,

Ulrich Eckhardt wrote:
> Therefore, the best way it to
> evaluate the pointer as a boolean expression, i.e. 'str!=0' and that
> yields 'true' or '1'.

But why the pointer *must* be evaluated as a boolean? Why not as a short
or an integer or another of the types in 27.6.2.5.2? Really, my question
is this one...

Thanks,
Paolo.

llewelly

unread,
Aug 23, 2004, 6:26:28 AM8/23/04
to
Paolo Carlini <pcar...@suse.de> writes:

> Hi,
>
> anyone is willing to explain why this snippet
>
> volatile char str[] = "a";
> std::cout << str;
>
> prints "1"? Or, in other terms, why operator<<(bool) is
> the best match?

There is no implicit conversion from 'char volatile*' to 'char const*'
. So operator<<(char const*) isn't a match. There is no
operator<<(char volatile*) or operator<<(char volatile const*) .

Paolo Carlini

unread,
Aug 23, 2004, 6:27:47 AM8/23/04
to
Paolo Carlini wrote:

> But why the pointer *must* be evaluated as a boolean? Why not as a short
> or an integer or another of the types in 27.6.2.5.2? Really, my question
> is this one...

Ok, now I know the answer: roughly, missing a perfect match, a match with
minor adjustments and a match with promotion, a match with standard
conversions is looked for: there is a standard conversion from pointer to
bool, *not* to other integral types.

This explanation I found in Vendevoorde/Josuttis appendix B and the standard
section 4.

Thanks again to everyone,

Allan W

unread,
Aug 24, 2004, 6:51:07 PM8/24/04
to
Paolo Carlini <carl...@tiscali.it> wrote

[Asks why
volatile char*str="x";
cout << str;
shows 1 -- it's converting to bool instead of char*]

> Ok, now I know the answer: roughly, missing a perfect match, a match with
> minor adjustments and a match with promotion, a match with standard
> conversions is looked for: there is a standard conversion from pointer to
> bool, *not* to other integral types.
>
> This explanation I found in Vendevoorde/Josuttis appendix B and the standard
> section 4.

I believe that the rationale goes back to classic C, before there was a
bool type. It was (still is, I suspect) common for code to do this:

const char*name = getname();
if (name) {
// Name was not null, so use it

Tokyo Tomy

unread,
Aug 25, 2004, 4:56:08 PM8/25/04
to
Paolo Carlini <pcar...@suse.de> wrote in message news:<2oomqsF...@uni-berlin.de>...

>
> anyone is willing to explain why this snippet
>
> volatile char str[] = "a";
> std::cout << str;
>
> prints "1"? Or, in other terms, why operator<<(bool) is
> the best match?
>

More type conversions seems to be involved.

volatile char* str -> volatile bool str -> bool str -> int str

Is this path legal according to the Standard?

ka...@gabi-soft.fr

unread,
Aug 26, 2004, 10:45:41 PM8/26/04
to
hos...@jtec.or.jp (Tokyo Tomy) wrote in message
news:<49c1da0b.04082...@posting.google.com>...

> Paolo Carlini <pcar...@suse.de> wrote in message
> news:<2oomqsF...@uni-berlin.de>...

> > anyone is willing to explain why this snippet

> > volatile char str[] = "a";
> > std::cout << str;

> > prints "1"? Or, in other terms, why operator<<(bool) is the best
> > match?

> More type conversions seems to be involved.

> volatile char* str -> volatile bool str -> bool str -> int str

> Is this path legal according to the Standard?

The path is "volatile char[2]" -> "volatile char*" -> bool. The result
of a conversion is an rvalue, and rvalues of non-class type (like bool)
don't have const-volatile qualifiers.

The standard says that by default, bool is output as if it were an
integer. In fact, although there is a boolalpha flag described in table
83 of §27.4.2.1.2, and the numpunct facet contains the functions
truename() and falsename(), §27.6.2.5.2 (formatted output for arithmetic
types, explicitly including bool) just refers to §22.2.2.2, the num_put
facet, and I can't find anything there which says that the boolalpha
flag is actually tested, or that truename() or falsename() may be used.
So unless I've missed something, you have to specialize the num_put
facet yourself and imbue a new locale to get anything but a numeric
value for a bool. (But I'm almost sure that this is an oversight, and
that I've missed something somewhere.)

--
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

Tokyo Tomy

unread,
Aug 27, 2004, 9:03:52 AM8/27/04
to
ka...@gabi-soft.fr wrote in message news:<d6652001.04082...@posting.google.com>...

> hos...@jtec.or.jp (Tokyo Tomy) wrote in message
> news:<49c1da0b.04082...@posting.google.com>...
> > Paolo Carlini <pcar...@suse.de> wrote in message
> > news:<2oomqsF...@uni-berlin.de>...
>
> > > anyone is willing to explain why this snippet
>
> > > volatile char str[] = "a";
> > > std::cout << str;
>
> > > prints "1"? Or, in other terms, why operator<<(bool) is the best
> > > match?
>
> > More type conversions seems to be involved.
>
> > volatile char* str -> volatile bool str -> bool str -> int str
>
> > Is this path legal according to the Standard?
>
> The path is "volatile char[2]" -> "volatile char*" -> bool. The result
> of a conversion is an rvalue, and rvalues of non-class type (like bool)
> don't have const-volatile qualifiers.
>
> ...

James Kanze, thank you for your kind explanation. I did not think my
guesswork above was correct. However, I still do not understand why
the code below fail to compile with my MSVC++6. Would somebody kindly
explain the reason?

#include <iostream>
using namespace std;

void f(int n ) { cout << n << endl; }
void f(char* str) { cout << str << endl; }
void f(const char* str) { cout << str << endl; }
void f(const char c) { cout << c << endl; }

int main( )
{


volatile char str[] = "a";

cout << str << endl;

f(str); // error
return 0;

ka...@gabi-soft.fr

unread,
Aug 30, 2004, 8:04:44 PM8/30/04
to
hos...@jtec.or.jp (Tokyo Tomy) wrote in message
news:<49c1da0b.04082...@posting.google.com>...

> However, I still do not understand why


> the code below fail to compile with my MSVC++6. Would somebody kindly
> explain the reason?

> #include <iostream>
> using namespace std;

> void f(int n ) { cout << n << endl; }
> void f(char* str) { cout << str << endl; }
> void f(const char* str) { cout << str << endl; }
> void f(const char c) { cout << c << endl; }

> int main( )
> {
> =09volatile char str[] = "a";
> =09cout << str << endl;

> =09f(str); // error
> =09return 0;
> }

Why should it compile? When passed to a function, an array degenerates
into a pointer to the first element. In this case, thus, you have a
char volatile*. An implicit conversion is not allowed to remove the
volatile, so none of the f's taking a pointer can be called. And a
pointer cannot convert implicitly to an arbitrary integral type, so none
of the f's taking an integral type (char, int) can be called.

--
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

Old Wolf

unread,
Aug 31, 2004, 8:10:55 AM8/31/04
to
Daryle Walker <dwal...@snet.net> wrote:
>
> What you're missing is that:
> "T *" -> "T const *" is implicit
> "T *" -> "T volatile *" is also implicit
> But
> "T volatile *" -> "T const *" can't be done implicitly! An explicit
> const_cast is needed, so [3] and [4] are _not_ considered.
>
> This means that the "bool" conversion is the only valid one. (We
> wouldn't have this problem if the pointer inserters used "char|void
> const volatile *" instead.)

I think this is a good thing. If a function expects a T const *,
and you pass it a T volatile *, you would get UB (presumably) if
the pointed-to item changed during the function's processing.
Why should the standard library bloat its code to support volatile
objects? The onus is on either:
a) the implementor of T, to provide a method that creates a
non-volatile T based on a volatile one, or
b) you, to do a const_cast<> when you know it is safe.

> This problem will get worse if we add C-1999's "restrict".

Not really, all the standard library functions put 'restrict'
everywhere, so I don't see where there would be a problem.
As with const and volatile, you can implicitly convert a
non-restrict to a restrict.

Tokyo Tomy

unread,
Aug 31, 2004, 3:29:12 PM8/31/04
to
ka...@gabi-soft.fr wrote in message news:<d6652001.0408...@posting.google.com>...

> hos...@jtec.or.jp (Tokyo Tomy) wrote in message
> news:<49c1da0b.04082...@posting.google.com>...
>
> > However, I still do not understand why
> > the code below fail to compile with my MSVC++6. Would somebody kindly
> > explain the reason?
> >
> > ...

>
> Why should it compile? When passed to a function, an array degenerates
> into a pointer to the first element. In this case, thus, you have a
> char volatile*. An implicit conversion is not allowed to remove the
> volatile, so none of the f's taking a pointer can be called. And a
> pointer cannot convert implicitly to an arbitrary integral type, so none
> of the f's taking an integral type (char, int) can be called.

James Kanze, thank you for your reply. Please excuse me for my
ignorance on C++, and let me ask you or others more.

Q1. James Kaze said


> An implicit conversion is not allowed to remove the
> volatile,

but if I add "void f(bool b)" to the previous code as shown below, the
code successfully complies with my MSVC++6 and the volatile seems to
be removed.

Q2. Is there any deference in looking-up methods between, std::ostream
& std::ostream::operator << ( char const * ) etc and f(char const* )
etc. In other words, if I provide f functions with all the same
combinations of arguments as std::ostream::operator << has or free
function std::ostream & operator << has, can f functions accept
volatile char* str or not? If not, would you kindly tell me how the
looling-up methods are deferent?

#include <iostream>
using namespace std;

void f(int n ) { cout << n << endl; }
void f(char* str) { cout << str << endl; }
void f(const char* str) { cout << str << endl; }

void f(char c) { cout << c << endl; }
void f(bool b) { cout << b << endl; } // newly added

int main()


{
volatile char str[] = "a";
cout << str << endl;

f(str); // ok

return 0;

ka...@gabi-soft.fr

unread,
Sep 1, 2004, 4:58:00 PM9/1/04
to
hos...@jtec.or.jp (Tokyo Tomy) wrote in message
news:<49c1da0b.04083...@posting.google.com>...

> ka...@gabi-soft.fr wrote in message
> news:<d6652001.0408...@posting.google.com>...
> > hos...@jtec.or.jp (Tokyo Tomy) wrote in message
> > news:<49c1da0b.04082...@posting.google.com>...

> > > However, I still do not understand
> > > why the code below fail to compile with my MSVC++6. Would somebody
> > > kindly explain the reason?

> > > ...

> > Why should it compile? When passed to a function, an array
> > degenerates into a pointer to the first element. In this case,
> > thus, you have a char volatile*. An implicit conversion is not
> > allowed to remove the volatile, so none of the f's taking a pointer
> > can be called. And a pointer cannot convert implicitly to an
> > arbitrary integral type, so none of the f's taking an integral type
> > (char, int) can be called.

> Q1. James Kaze said


> > An implicit conversion is not allowed to remove the
> > volatile,
> but if I add "void f(bool b)" to the previous code as shown below, the
> code successfully complies with my MSVC++6 and the volatile seems to
> be removed.

I should have benn more precise. When I said that an implicit
conversion is not allowed to remove volatile or const. An implicit
conversion of a pointer or a reference is not allowed to remove the
volatile or the const of what is pointed to or referenced. When you
convert a pointer to bool, the situation is somewhat different -- after
the conversion, there is nothing that is pointed to. Basically, there
are a certain number of implicit conversions T* to U*, but they are very
limited; in particular, U may have more const-volatile qualifiers than
T, but not less. If T and U are themselves pointers, the rules get even
more complicated, but the overall rule is to preserve the semantics of
const and volatile -- if the conversion could, in some way, allow you to
end up accessing something which was declared const or volatile through
an expression which doesn't consider it const or volatile, the
conversion cannot be implicit. In the case of T* to bool, the rule is
that the implicit conversion is legal regardless of what T is. Thus, it
can take place if T is char, but also if T is const char or volatile
char.

> Q2. Is there any deference in looking-up methods between, std::ostream
> & std::ostream::operator << ( char const * ) etc and f(char const* )
> etc. In other words, if I provide f functions with all the same
> combinations of arguments as std::ostream::operator << has or free
> function std::ostream & operator << has, can f functions accept
> volatile char* str or not? If not, would you kindly tell me how the
> looling-up methods are deferent?

No. The look-up rules are basically identical. (There are some
differences in the way function overload resolution works in the case of
a named function and in the case of an operator, but they don't concern
us here.) If you provide the same name function overloads, the compiler
should do the same overload resolution, with the same results.

If you provide functions f( char* ) and f( char const* ), you cannot
call either of them with a volatile char*, and they will not be
considered in overload resolution.

> #include <iostream>
> using namespace std;

> void f(int n ) { cout << n << endl; }
> void f(char* str) { cout << str << endl; }
> void f(const char* str) { cout << str << endl; }
> void f(char c) { cout << c << endl; }
> void f(bool b) { cout << b << endl; } // newly added

> int main()
> {
> volatile char str[] = "a";
> cout << str << endl;

> f(str); // ok

Yes. I will call f(bool).

> return 0;
> }

--
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

Tokyo Tomy

unread,
Sep 2, 2004, 8:10:00 AM9/2/04
to
ka...@gabi-soft.fr wrote in message news:<d6652001.04090...@posting.google.com>...

>
> > Q2. Is there any deference in looking-up methods between, std::ostream
> > & std::ostream::operator << ( char const * ) etc and f(char const* )
> > etc. In other words, if I provide f functions with all the same
> > combinations of arguments as std::ostream::operator << has or free
> > function std::ostream & operator << has, can f functions accept
> > volatile char* str or not? If not, would you kindly tell me how the
> > looling-up methods are deferent?
>
> No. The look-up rules are basically identical. (There are some
> differences in the way function overload resolution works in the case of
> a named function and in the case of an operator, but they don't concern
> us here.) If you provide the same name function overloads, the compiler
> should do the same overload resolution, with the same results.
>
> If you provide functions f( char* ) and f( char const* ), you cannot
> call either of them with a volatile char*, and they will not be
> considered in overload resolution.
>

Thank you for your reply, Kanze. As I am not a paranoia, let this
question be my last one in this thread and let me express my
appreciation in advance.

In the code below, I am not yet sure why the line (1) works and the
line (2) does not work. The line (1) works on the ostream::operator
<<, while the line (2) works on the f functions which have the all
overloadings as the the ostream::operator << provides, except for the
three overloadings commented out in the below code.

If the same looking-up rules are applied for the both the operator <<
and function f, I think the line (2) should work as the line (1) do.

Maybe, I have missed something in my argument.

#include <iostream>

void f( char ch ){};
void f( unsigned char uch ){};
void f( signed char sch ){};
void f( const char* psz ){};
void f( const unsigned char *pusz ){};
void f( const signed char *pssz ){};
void f( short s ){};
void f( unsigned short us ){};
void f( int n ){};
void f( unsigned int un ){};
void f( long l ){};
void f( unsigned long ul ){};
void f( float f ){};
void f( double d ){};
void f( long double ld ){};
void f( const void* pv ){};

//ostream& operator <<( streambuf* psb );
//ostream& operator <<( ostream& (*fcn)(ostream&) );
//ostream& operator <<( ios& (*fcn)(ios&) );

int main()
{
volatile char str[] = "a";

std::cout << str; // (1) ok

f(str); // (2) error

return 0;

ka...@gabi-soft.fr

unread,
Sep 3, 2004, 9:49:57 PM9/3/04
to
hos...@jtec.or.jp (Tokyo Tomy) wrote in message
news:<49c1da0b.04090...@posting.google.com>...

[...]


> Thank you for your reply, Kanze. As I am not a paranoia, let this
> question be my last one in this thread and let me express my
> appreciation in advance.

Trying to understand a complicated issue is NOT being paranoid. (And
C++ overload resolution certainly qualifies as a complicated issue.)

> In the code below, I am not yet sure why the line (1) works and the
> line (2) does not work. The line (1) works on the ostream::operator
> <<, while the line (2) works on the f functions which have the all
> overloadings as the the ostream::operator << provides, except for the
> three overloadings commented out in the below code.

Not quite.

> If the same looking-up rules are applied for the both the operator <<
> and function f, I think the line (2) should work as the line (1) do.

> Maybe, I have missed something in my argument.

Yes. You've missed one of the operator<< overloads.

> #include <iostream>

> void f( char ch ){};
> void f( unsigned char uch ){};
> void f( signed char sch ){};
> void f( const char* psz ){};
> void f( const unsigned char *pusz ){};
> void f( const signed char *pssz ){};
> void f( short s ){};
> void f( unsigned short us ){};
> void f( int n ){};
> void f( unsigned int un ){};
> void f( long l ){};
> void f( unsigned long ul ){};
> void f( float f ){};
> void f( double d ){};
> void f( long double ld ){};
> void f( const void* pv ){};

Try adding:

void f( bool ) {}

It's the operator<< you've missed.

> //ostream& operator <<( streambuf* psb );
> //ostream& operator <<( ostream& (*fcn)(ostream&) );
> //ostream& operator <<( ios& (*fcn)(ios&) );

> int main()
> {
> volatile char str[] = "a";
> std::cout << str; // (1) ok

And it is the operator << which is getting called here.

> f(str); // (2) error

> return 0;
> }

--


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 ]

Tokyo Tomy

unread,
Sep 4, 2004, 6:42:48 AM9/4/04
to
ka...@gabi-soft.fr wrote in message news:<d6652001.04090...@posting.google.com>...
> hos...@jtec.or.jp (Tokyo Tomy) wrote in message
> news:<49c1da0b.04090...@posting.google.com>...
> > ka...@gabi-soft.fr wrote in message
> > news:<d6652001.04090...@posting.google.com>...
>
> [...]
> Try adding:
>
> void f( bool ) {}
>
> It's the operator<< you've missed.
>
> ...

Thank you for your reply, James Kanze. This is not my question, but my
apology for you, since I could not find the ostream::operator <<
(bool) in the help of my MSVC++6 compiler. The problem for me is I
cannot still find it in the help. Maybe the help is one of broken
ones. Thank you very much, James Kanze.

0 new messages