Consider two pointers
A* a;
B* b;
Both A and B are polymorphic classes. How to check whether a and b point to the same object or not?
More precisely, let's specify a and b point to the same object if there exists some object d of type D such that both *a and *b are somewhere in the class hierarchy of d.
I would propose the following solution:
dynamic_cast<void*>(a) == dynamic_cast<void*>(b)
Indeed, according to the standard,
dynamic_cast<void*>(v)
yields ”a pointer to the most derived object pointed to by v. (n3242.pdf: § 5.2.7 - 7). If the most derived for both is the same object, then the pointers point to the same object.
I'm pretty sure that it should always work correctly from the practical viewpoint. But theoretically, at first glance the proposed equality seems to produce false positive, for example, in case if b points to the first member of A (not to A's ancestor). Although it's practically impossible to get equal addresses for A and its member since A's virtual table pointer should be located before this member, the standard doesn't mandate virtual tables and says nothing about the class layout.
So, my questions are:
Is the proposed solution correct from the standard viewpoint?
Are there any caveats about private (protected) inheritance or cv-qualification ?
Are there better solutions?
[EDIT]
I tried to present some example that illustrates a relatively complex scenario. In this case dynamic crosscasting and static casting are ambiguous.
// proposed impplementation:
template<typename P, typename Q>
bool test_ptrs(const P* p, const Q* q)
{
return (dynamic_cast<const void*>(p) == dynamic_cast<const void*>(q));
}
struct Root
{
virtual ~Root(){};
};
struct A: public Root // nonvirtually
{
};
struct B: public Root // nonvirtually
{
};
struct C: public A, B // nonvirtual diamond started with Root
{
Root another_root_instance;
};
int main()
{
C c;
A* pa= &c;
B* pb= &c;
bool b = (dynamic_cast<void*>(pa) == dynamic_cast<void*>(pb));
Root* pra= dynamic_cast<Root*> (pa);
Root* prb= dynamic_cast<Root*> (pb);
//Root* prc= dynamic_cast<Root*> (&c); // runtime error, ambiguous cast
Root* prr= dynamic_cast<Root*>(pra);
Root* pcar= dynamic_cast<Root*>(pra);
Root* pcbr= dynamic_cast<Root*>(prb);
if(
test_ptrs(pa, pb)
&& test_ptrs(pra, prb)
&& !test_ptrs(pa,&c.another_root_instance)
)
{
printf("\n test passed \n");
}
}
It would seem to me the least smelly way to deal with this is to introduce a base class for A & B:
#include <iostream>
struct Base
{
virtual ~Base() {};
};
struct A : public virtual Base
{
int a;
virtual ~A() {};
virtual void afunc() {};
};
struct B : public virtual Base
{
int b;
virtual ~B() {};
virtual void bfunc() {};
};
struct C: A, B
{};
int main()
{
C c;
A *a = &c;
B *b = &c;
std::cout << "a* == " << &(*a) << std::endl;
std::cout << "b* == " << &(*b) << std::endl;
std::cout << "a == b == " << ((void*)a == (void*)b) << std::endl;
Base* ba = a;
Base* bb = b;
std::cout << "ba* == " << &(*ba) << std::endl;
std::cout << "bb* == " << &(*bb) << std::endl;
std::cout << "ba == bb == " << (ba == bb) << std::endl;
return 0;
}