I have the following code that I run in Visual Studio. The address of c
is the same as the address to which points pa
but not the same as pb
. Yet both ternary operator will evaluate as true
, which is what would have expected by only viewing the code and not see the pointed addresses for pa and pb
in debugger.
The third ternary operator will evaluate as false
.
#include <iostream>
class A
{
public:
A() : m_i(0) {}
protected:
int m_i;
};
class B
{
public:
B() : m_d(0.0) {}
protected:
double m_d;
};
class C
: public A
, public B
{
public:
C() : m_c('a') {}
private:
char m_c;
};
int main()
{
C c;
A *pa = &c;
B *pb = &c;
const int x = (pa == &c) ? 1 : 2;
const int y = (pb == &c) ? 3 : 4;
const int z = (reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)) ? 5 : 6;
std::cout << x << y << z << std::endl;
return 0;
}
How does this work?
pa
and pb
are actually different. One way to test that is:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)
pa == &c
and pb == &c
both return true
, but that does not mean the above must be true
. &c
will be converted to appropriate pointer type (A*
or B*
) via implicit pointer conversion. This conversion changes the pointer's value to the address of respective base class subobject of the object pointed-to by &c
.
From cppreference:
A prvalue pointer to a (optionally cv-qualified) derived class type can be converted to a prvalue pointer to its accessible, unambiguous (identically cv-qualified) base class. The result of the conversion is a pointer to the base class subobject within the pointed-to object. The null pointer value is converted to the null pointer value of the destination type.
(emphasis mine)
A
is the first non-virtual base class of C
, so it is placed directly at the beginning of C
's memory space, i.e.:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c)
is true
. But, B
subobject is laid out after A
, so it can not possibly satisfy the above condition. Both implicit conversion and static_cast
then gives you the right address of the base subobject.