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.