1. Introduction
Printing pointers to
types with standard library output streams has
unexpected results.
Consider the following code:
#include <iostream>int main () { int * p0 = reinterpret_cast < int *> ( 0xdeadbeef ); volatile int * p1 = reinterpret_cast < volatile int *> ( 0xdeadbeef ); std :: cout << p0 << std :: endl ; std :: cout << p1 << std :: endl ; }
This produces the following output:
0xdeadbeef 1
What happened here? Well,
has an
for both
and
:
27.7.5.1 Class template[ostream]
basic_ostream namespace std { template < class charT , class traits = char_traits < charT >> class basic_ostream : virtual public basic_ios < charT , traits > { public : // ... // [ostream.formatted], formatted output // ... basic_ostream < charT , traits >& operator << ( bool n ); basic_ostream < charT , traits >& operator << ( const void * p ); // ... // ... }; }
For
,
(an
) is implicitly converted to
and
is called.
However, for
, the
overload is not a
match, as it discards qualifiers. Instead, the best match is
, so
(a
) is implicitly converted to
and
is called.
I suggest we add a new
overload that
s away
and calls the
overload.
Initially, I explored modifying the underlying
methods and changing
the existing
to
,
however, early feedback from other committee members pushed me away from this
direction, as there was concern that modifying
would be an ABI
breaking change.
Note that
ing away
is safe here.
We are not accessing or printing the value of the object that the pointer
points to, we are just printing the value of the pointer itself.
2. Wording
Modify [ostream.general] as follows:
29.7.5.2.1 General [ostream.general]
namespace std { template < class charT , class traits = char_traits < charT >> class basic_ostream : virtual public basic_ios < charT , traits > { public : // ... // [ostream.formatted], formatted output basic_ostream & operator << ( basic_ostream & ( * pf )( basic_ostream & )); basic_ostream & operator << ( basic_ios < charT , traits >& ( * pf )( basic_ios < charT , traits >& )); basic_ostream & operator << ( ios_base & ( * pf )( ios_base & )); basic_ostream & operator << ( bool n ); basic_ostream & operator << ( short n ); basic_ostream & operator << ( unsigned short n ); basic_ostream & operator << ( int n ); basic_ostream & operator << ( unsigned int n ); basic_ostream & operator << ( long n ); basic_ostream & operator << ( unsigned long n ); basic_ostream & operator << ( long long n ); basic_ostream & operator << ( unsigned long long n ); basic_ostream & operator << ( float f ); basic_ostream & operator << ( double f ); basic_ostream & operator << ( long double f ); basic_ostream & operator << ( const void * p ); basic_ostream & operator << ( const volatile void * val ); basic_ostream & operator << ( nullptr_t ); basic_ostream & operator << ( basic_streambuf < char_type , traits >* sb ); // ... }; // ... }
Modify [ostream.inserters] as follows:
29.7.5.3.3[ostream.inserters]
basic_ostream :: operator << ...
basic_ostream & operator << ( nullptr_t ); Effects: Equivalent to:
return * this << s ; where
is an implementation-defined NTCTS.
s
basic_ostream & operator << ( const volatile void * val ); Effects: Equivalent to:
return operator << ( const_cast < const void *> ( val ));
3. Acknowledgements
Thanks to JF Bastien, Marshall Clow, Billy O’Neal, Louis Dionne, Jonathan Wakely, and Jeff Garland for reviewing and providing feedback on this paper.