Why does std::cout convert volatile pointers to bool?

If you try to cout a pointer to a volatile type, even a volatile char pointer where you would normally expect cout to print the string, you will instead simply get '1' (assuming the pointer is not null I think). I assume output stream operator<< is template specialized for volatile pointers, but my question is, why? What use case motivates this behavior?

Example code:

#include <iostream>
#include <cstring>

int main()
{
    char x[500];
    std::strcpy(x, "Hello world");

    int y;
    int *z = &y;

    std::cout << x << std::endl;
    std::cout << (char volatile*)x << std::endl;

    std::cout << z << std::endl;
    std::cout << (int volatile*)z << std::endl;

    return 0;
}

Output:

Hello world
1
0x8046b6c
1

ostream::operator<< has the following overloads, among others:

ostream& operator<< (bool val );
ostream& operator<< (const void* val );

When you pass in a volatile pointer, the second overload can't apply because volatile pointers cannot be converted to non-volatile without an explicit cast. However, any pointer can be converted to bool, so the first overload is chosen, and the result you see is 1 or 0.

So the real reason for this is not an intentional decision on behalf of the standards committe, but simply that the standard does not specify an overload that takes a volatile pointer.


I think the reason is that volatile pointers cannot be converted implicitly to void *. This is in Appendix C of the Standard, and the rationale is type safety.

Change: Only pointers to non-const and non-volatile objects may be implicitly converted to void* Rationale: This improves type safety.

So instead of the conversion to void * (which would print in hex), you get the "default" conversion to bool.


Not an answer

This is just an issue with the wording of the question and the answers. The problem arises due to the inability to convert pointers to volatile objects into void pointers, not volatile pointers.

The difference, which is rather important, is what memory element is the one that is volatile. In the question, the pointer is not volatile (it can be cached, and it does not have to be flushed to memory when it is changed), but rather the pointed memory:

int volatile * p = f();
p++;      // this does not affect the perceived state of the c++ memory model
++p;
*p = 5;   // this changes the perceived state

The reason why it is important is that with a volatile pointer to memory, the pointer itself is the one that has special treatment.

void foo( int * );

int * volatile p = f();  // 1
foo(p);                  // 2
int volatile * q = f();
//foo(q);    // error, cannot convert pointer to volatile to pointer to non-volatile
*q = 5;                  // 3
q = 0;                   // 4

In the code above, the operations marked as 1 and 2 make it all the way to memory. The assignment in [1] must be dumped to memory. Even if the value of p is in a register, it will be loaded from memory at [2]. The operation marked [3] modifies the value pointed by q which is volatile and will make all the way to main memory, while the operation [4] only affects the pointer, which is not volatile itself, and as such is not part of the c++ memory model perceivable state and can be performed in registers (note that a compiler can optimize away q and perform the operations in a register, while p cannot be optimized.


I think the problem is not an explicit overload for pointers to volatile types, but a LACK of overload for pointers to volatile types. The compiler can't implicitly remove the volatile qualifier from the pointers so it checks available overloads, picks the bool version of operator<< and converts the pointer-to-volatile to bool.