4

Say I want to check to see whether a subclass has implemented one of it's parent's virtual functions (never mind whether this smells of bad architecture... it's an exercise). If I wanted to see if two regular functions were identical, I could just check &f == &g.

// Plain old functions
void f() {}
void g() {}

...

std::cout << "&f " << &f << "\n";               // "1"  OK, for some reason func ptrs are converted
std::cout << "&g " << &f << "\n";               // "1"  to booleans when printed. I can dig it.
std::cout << "&f == &g " << (&f == &g) << "\n"; // "0"  Good, &f and &g are unequal as expected.

But with virtual member functions, behavior is different.

// Base class with a virtual
struct A {
    virtual void f() {}
};

// Subclass which implements f
struct B : public A {
    void f() {}
};

// Subclass which doesn't implement f
struct C : public A {};

...

std::cout << "&A::f " << &A::f << "\n"; // "1"
std::cout << "&B::f " << &B::f << "\n"; // "1"
std::cout << "&C::f " << &C::f << "\n"; // "1" ... okay ... annoying, but alright.

std::cout << "&A::f == &B::f " << (&A::f == &B::f) << "\n"; // "1" DANGER - why does &A::f == &B::f if &f != &g?
std::cout << "&A::f == &C::f " << (&A::f == &C::f) << "\n"; // "1"
std::cout << "&B::f == &C::f " << (&B::f == &C::f) << "\n"; // "1"

std::cout << "(void*)&A::f " << (void*)&A::f << "\n";   // "0x4084b0" Here's what I was actually looking for.
std::cout << "(void*)&B::f " << (void*)&B::f << "\n";   // "0x4084bc" Good - the B::f differs from A::f as it should
std::cout << "(void*)&C::f " << (void*)&C::f << "\n";   // "0x4084b0" Perfect.

std::cout << "(void*)&A::f == (void*)&B::f " << ((void*)&A::f == (void*)&B::f) << "\n"; // "0"
std::cout << "(void*)&A::f == (void*)&C::f " << ((void*)&A::f == (void*)&C::f) << "\n"; // "1"
std::cout << "(void*)&B::f == (void*)&C::f " << ((void*)&B::f == (void*)&C::f) << "\n"; // "0" These are the comparison results I want

So my question is marked by DANGER in the code above. Why does &A::f == &B::f if &f != &g? Is there a way to do the comparison I want without casting to void* (which gives off noisy compiler warnings thanks to -Wpmf-conversions)?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
stett
  • 1,351
  • 1
  • 11
  • 24
  • The fact that `&C::f` prints `1` seems a little bit curious. I'd be interested what the overload function is that gets called... Another interesting aspect is that `clang++` won't even compile your casts to pointer. – Mats Petersson Nov 19 '15 at 20:59
  • 1
    I'm starting to believe this is not possible. In the sense that a pointer to a virtual function is not a pointer to a function in itself, it is a the index into the vtable, so you can call the "correct" function regardless of what the actual class is. – Mats Petersson Nov 19 '15 at 21:09
  • If you don't mind code that is pretty undefined, but since clang won't compile the code, what you have posted is also unportable, one way to solve it is to take a peek inside the vtable. [But it gets messy if you have more than one baseclass] – Mats Petersson Nov 19 '15 at 21:11
  • @MatsPetersson, I was kind of wondering the same thing as you mentioned in your second comment. It seems like the cast to (void*) causes the address of the real function to be pulled out of the vtable, which I was hoping was standard but couldn't find it in documentation. I suppose this must be a g++ thing, and also the un-portable bit, but I'll keep looking. – stett Nov 19 '15 at 21:14

0 Answers0