Most discussion I've seen about member pointers focus on conversions permitted on the type to which the member belongs. My question is about conversions on the type of the member.
struct Base{};
struct Derived : public Base{};
struct Foo{ Derived m_Derived; };
Given these declarations, the following code produces an error (MSVC 2008):
// error C2440: 'initializing' : cannot convert from 'Derived Foo::* ' to 'Base Foo::* '
Base Foo::*p = &Foo::m_Derived;
Conversion from Derived* to Base* is normally allowed - why the difference here?
There are two types of conversion: implicit and explicit. The term for implicit type conversion is coercion. Explicit type conversion in some specific way is known as casting. Explicit type conversion can also be achieved with separately defined conversion routines such as an overloaded object constructor.
Implicit conversions: No special syntax is required because the conversion always succeeds and no data will be lost. Examples include conversions from smaller to larger integral types, and conversions from derived classes to base classes.
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
Implicit type conversion in C language is the conversion of one data type into another datatype by the compiler during the execution of the program. It is also called automatic type conversion.
You have the variance backward.
Return types convert implicitly to base types (contravariance). But parameters convert implicitly to derived types (covariance), and the class type in a pointer-to-member acts as a parameter. To see this, let's apply the Liskov Substitutability Principle:
The contract of Base*
is: "I will give you a Base" (when you use the *
operator on me).
The contract of Derived*
is "I will give you a Derived, which is also a Base".
Clearly a Derived*
can be used in place of a Base*
. Therefore there is an implicit conversion from Derived*
to Base*
.
But consider the contract of a pointer-to-member.
The contract of int Base::*
is: "Give me a Base and I will give you back an int" (A Derived is a Base, so those are ok too)
The contract of int Derived::*
is: "Give me a Derived and I will give you back an int" (But not any old Base
will do, it must be a Derived
)
Imagine that you have a Base
which is not a Derived
. It will work nicely when dereferencing an int Base::*
, but cannot be used with an int Derived*
).
However, if you have a Derived
, you can use it to dereference both int Base::*
and int Derived::*
. Therefore there is an implicit conversion from int Base::*
to int Derived::*
Argh, I did what you said and analyzed the type that the member belongs to.
The LSP still works though. And I agree that the conversion should be legal, at least according to type safety. The contract is "Give me a Foo
and I will give you a Derived
", which clearly you can use to get from Foo
to Base
by composing it with an implicit conversion. So it's safe. DeadMG is probably on the right track pointing out the potential complication with the relation location of the base subobject, especially in virtual inheritance. But pointer-to-members deal with these problems on the LHS of the dereference operator, so they could for the result as well.
Final answer is probably simply that the standard doesn't require that the conversion is legal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With