9

I am playing with pointer-to-members and decided to actually print the values of the pointers. The result was not what I expected.

#include <iostream>

struct ManyIntegers {

    int a,b,c,d;
};

int main () {

    int ManyIntegers::* p;

    p = &ManyIntegers::a;
    std::cout << "p = &ManyIntegers::a = " << p << std::endl; // prints 1

    p = &ManyIntegers::b;
    std::cout << "p = &ManyIntegers::b = " << p << std::endl; // prints 1

    p = &ManyIntegers::c;
    std::cout << "p = &ManyIntegers::c = " << p << std::endl; // prints 1

    p = &ManyIntegers::d;
    std::cout << "p = &ManyIntegers::d = " << p << std::endl; // prints 1

    return 0;
}

Why is the value of p always 1? Shouldn't the value of p somehow reflect which class member it points to?

6 Answers6

9

As everyone has said, ostream doesn't have the appropriate operator<< defined.

Try this:

#include <cstddef>
#include <iostream>

struct Dumper {
  unsigned char *p;
  std::size_t size;
  template<class T>
  Dumper(const T& t) : p((unsigned char*)&t), size(sizeof t) { }
  friend std::ostream& operator<<(std::ostream& os, const Dumper& d) {
    for(std::size_t i = 0; i < d.size; i++) {
      os << "0x" << std::hex << (unsigned int)d.p[i] << " ";
    }
    return os;
  }
};

#include <iostream>

struct ManyIntegers {

    int a,b,c,d;
};

int main () {

    int ManyIntegers::* p;

    p = &ManyIntegers::a;
    std::cout << "p = &ManyIntegers::a = " << Dumper(p) << "\n"; 

    p = &ManyIntegers::b;
    std::cout << "p = &ManyIntegers::b = " << Dumper(p) << "\n"; 

    p = &ManyIntegers::c;
    std::cout << "p = &ManyIntegers::c = " << Dumper(p) << "\n"; 

    p = &ManyIntegers::d;
    std::cout << "p = &ManyIntegers::d = " << Dumper(p) << "\n"; 

    return 0;
}
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • This is robust. "Union cast" [also works](http://ideone.com/M64WG), but I'm not sure there exists a numeric type that is guaranteed to be at least the size of a pointer-to-member. – Magnus Hoff Aug 24 '12 at 13:54
  • @MagnusHoff There is no numeric size which is guaranteed, but for pointer to data member, it's fairly safe to assume that the size will be less than or equal to the size of a `size_t`, since the only information needed is the offset. For pointer to member function, on the other hand, the issue is more complex, and on a 64 bit machine, it wouldn't surprise me if they were 16 bytes, and longer than any integral type. – James Kanze Aug 24 '12 at 14:00
  • I like it. My own implementation has always used a class template, with a function template which returns it to benefit from type deduction; this is a much simpler way of achieving the same end. Note that it only really works if the `Dumper` is a temporary, however; otherwise, if you're dumping a temporary, you have lifetime of object issues. (This isn't a problem in practice, but it's something worth documenting if you're putting an instance of this in your toolbox.) – James Kanze Aug 24 '12 at 14:03
7

Standard ostream operator<< has no overload for pointer to member, so you pointer has been implicitly converted to bool.

Rost
  • 8,779
  • 28
  • 50
  • Well, if I instead had `int* p = new int(5);`, then std::cout << p << std::endl would actually print the address of the created integer, so I am guessing from this that `operator<<` has an overload for pointers. I'm then infering that a pointer-to-member is a different type of pointer which std::cout does not recognize. –  Aug 24 '12 at 13:38
  • @curvature absolutely correct, the wording in the answer was wrong. – Luchian Grigore Aug 24 '12 at 13:39
  • 2
    `reinterpret_cast(p)` [triggers compile errors](http://ideone.com/C0O8u). – Sergey Kalinichenko Aug 24 '12 at 13:40
  • 1
    @curvative I meant pointer to member which is different type, just skipped it for brevity. Sorry for inconvience. – Rost Aug 24 '12 at 13:40
  • 3
    Why in hell does this have 4 upvotes? The solution provided **does not work!**. – mfontanini Aug 24 '12 at 13:43
  • Pointer to member casting to integral is denied, see http://stackoverflow.com/questions/1307278/casting-between-void-and-a-pointer-to-member-function – Rost Aug 24 '12 at 13:51
  • 1
    Is this legal? I can't find anything in the standard which would allow using `reinterpret_cast` to convert from pointer to member to an integral type. The only way I know of doing this legally would be `*reinterpret_cast(&p)`, where `unsigned` should be replaced by an appropriately sized integral type. – James Kanze Aug 24 '12 at 13:51
  • @JamesKanze No, it's illegal, sorry for inconvience. – Rost Aug 24 '12 at 14:00
3

p actually contains offset in object. Printing them prints implicit converted bool value true or false if they really contains some offset or not respectively. Conversion happens due to the fact that ostream's insertion member doesn't have any overload for pointers to members.

Magnus Hoff
  • 21,529
  • 9
  • 63
  • 82
Mr.Anubis
  • 5,132
  • 6
  • 29
  • 44
  • What `p` actually contains is unspecified. All that is specified is the results of using `p` in different types of expressions. (In the most frequently used implementation technique, `p` contains the offset + 1, so that a null pointer to member can be all 0 bits.) – James Kanze Aug 24 '12 at 13:57
  • I agree with you , though in practice most compilers contains the offset as your said :) – Mr.Anubis Aug 24 '12 at 14:02
  • For what it's worth, ideone's null pointer-to-members seem to have all bits set to 1: http://ideone.com/jpwXP – Magnus Hoff Aug 24 '12 at 14:07
  • @MagnusHoff That's probably the solution I would adopt:-). But it's not the solution I've seen with existing compilers (g++ and VC++, I think: I know I've looked at one or two, but I'm no longer 100% sure which). – James Kanze Aug 24 '12 at 14:23
2

There is no overload of operator<< which takes pointer-to-member as argument. So if you try printing pointer-to-member, it implicitly converts into true which gets passed to the overload which takes bool as argument, and it prints 1 corresponds to true.

If you use std::boolalpha stream-manipulator, it will print true instead of 1:

std::cout << std::boolalpha << "p = &ManyIntegers::a = " << p ;
           //^^^^^^^^^^^^^^

Output (see at ideone):

p = &ManyIntegers::a = true

Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

A member pointer isn't necessarily a numeric value, more often than not it will be a struct or something the like. I don't think there is a way to obtain a value from a member pointer, but even if there is I don't see how that would be useful.

Cubic
  • 14,902
  • 5
  • 47
  • 92
0

Here's a fully standards-compliant implementation to show the in-memory representation of the pointer-to-members:

#include <iostream>
#include <iomanip>

template<int... I> struct index_tuple { using succ = index_tuple<I..., sizeof...(I)>; };
template<int I> struct indexer { using type = typename indexer<I - 1>::type::succ; };
template<> struct indexer<0> { using type = index_tuple<>; };
template<typename T> typename indexer<sizeof(T)>::type index(const T &) { return {}; }

template<typename T> class dumper {
    unsigned char buf[sizeof(T)];
    friend std::ostream &operator<<(std::ostream &os, const dumper &o) {
        std::ios_base::fmtflags flags{os.flags()};
        std::copy_n(o.buf, sizeof(T),
            std::ostream_iterator<int>(os << std::hex << std::showbase, " "));
        return os << std::setiosflags(flags);
    }
    template<int... I> dumper (const T &t, index_tuple<I...>):
        buf{reinterpret_cast<const unsigned char *>(&t)[I]...} {}
public:
    dumper(const T &t): dumper(t, index(t)) {}
};
template<typename T> dumper<T> dump(const T &t) { return {t}; }

struct ManyIntegers {
    int a,b,c,d;
};

int main () {
    std::cout << "p = &ManyIntegers::a = " << dump(&ManyIntegers::a) << std::endl;
    std::cout << "p = &ManyIntegers::b = " << dump(&ManyIntegers::b) << std::endl;
    std::cout << "p = &ManyIntegers::c = " << dump(&ManyIntegers::c) << std::endl;
    std::cout << "p = &ManyIntegers::d = " << dump(&ManyIntegers::d) << std::endl;
}

Output is as expected:

p = &ManyIntegers::a = 0 0 0 0 
p = &ManyIntegers::b = 0x4 0 0 0 
p = &ManyIntegers::c = 0x8 0 0 0 
p = &ManyIntegers::d = 0xc 0 0 0 
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I guess this requires some explanation. Can you explain in a few words what you are doing there? – Björn Pollex Aug 24 '12 at 17:32
  • @BjörnPollex I'm copying the in-memory contents of the member-to-pointer into an `unsigned char` buffer in order to print it out. The indexing tricks are just to perform the copy efficiently. – ecatmur Aug 24 '12 at 18:20