There is no operator<<(std::ostream&, T)
defined for T = &C::m
. Normally you'd get an error.
But instead, there is one for T = bool
and an implicit conversion from a member-pointer to bool
. So the output you're seeing is just the result of those pointers not being null (being converted to true
).
Try this, for example:
#include <iomanip>
#include <iostream>
struct A
{
int x, y, z;
};
int main()
{
std::cout << std::boolalpha; // print boolean values as text
std::cout << &A::x << std::endl;
std::cout << &A::y << std::endl;
std::cout << &A::z << std::endl;
}
Output:
true
true
true
Note that in the code printf("%p", &A::X)
, you have undefined behavior.
The value for the %p
specifier must be a void*
, and there is no conversion from a member-pointer to a void*
. Instead, what you have aliases (type-puns) to void*
, which is undefined behavior. (Imagine that sizeof(&A::x)
was 4 while sizeof(void*)
was 64; nothing says this cannot be the case.)
You just have to come to terms with the idea that not all pointers can be viewed as integer offsets. That we can even print a pointer is implementation-defined: it could print "apple" for null, "pear" for one value, and "milk" for another if a (dumb) implementation wanted to. I've touched on this difference between values and their representations before.
And in this case, there is no output for the value at all. And that's okay, not all values have meaningful printed outputs. The most you can do is print out the individual bits:
#include <climits>
#include <iostream>
#include <type_traits>
template <typename T>
auto print_bits(const T& value)
-> typename std::enable_if<std::is_standard_layout<T>::value>::type
{
// it's okay to alias a standard-layout type as a sequence of bytes:
const auto valueAsBytes = reinterpret_cast<const unsigned char*>(&value);
for (std::size_t byte = 0; byte < sizeof(T); ++byte)
{
// print in reverse order
const std::size_t byteIndex = sizeof(T) - byte - 1;
const unsigned char byteValue = valueAsBytes[byteIndex];
for (std::size_t bit = 0; bit < CHAR_BIT; ++bit)
{
// print in reverse order
const std::size_t bitIndex = CHAR_BIT - bit - 1;
const bool bitValue = (byteValue & (1U << bitIndex)) != 0;
std::cout << (bitValue ? 1 : 0);
}
std::cout << ' ';
}
std::cout << std::endl;
}
(I print the bytes and bits in reverse order because on my architectures this puts the least-significant bit on the right. I prefer to view binary values this way.)
This gives:
struct A
{
int x, y, z;
};
int main()
{
// example:
for (unsigned i = 0; i < 64; ++i)
print_bits(i);
std::cout << std::endl;
// member-pointers:
print_bits(&A::x);
print_bits(&A::y);
print_bits(&A::z);
}
Output:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
[...]
00000000 00000000 00000000 00111101
00000000 00000000 00000000 00111110
00000000 00000000 00000000 00111111
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000100
00000000 00000000 00000000 00001000
There are no guarantees on what you see for the member-pointers.