C ++ 03 std, §4.11 2 Указатель на преобразование члена :
Значение типа «указатель на член B типа cv T», где B - тип класса, может быть преобразовано в значение типа «указатель на член D типа * 1008». * cv T, ”где D - это производный класс (пункт 10) класса B. Если B - недоступный (пункт 11), неоднозначный (10.2) или виртуальный (10.1) базовый класс D, программа, для которой это необходимо обращение плохо сформировано. Результат преобразования ссылается на тот же элемент, что и указатель на член до преобразования, но ссылается на элемент базового класса, как если бы он был членом производного класса. Результат ссылается на член в экземпляре D из B. Поскольку результат имеет тип «указатель на член D типа cv T», его можно разыменовать с помощью объекта D. Результат такой же, как если бы указатель на член B был разыменован с подобъектом B объекта D. Значение указателя с нулевым элементом преобразуется в значение указателя с нулевым элементом типа назначения. 52)
52) Правило для преобразования указателей на элементы (от указателя на элемент базы к указателю на элемент производного) выглядит инвертированным по сравнению с правилом для указателей на объекты (от указателя на производный к указателю) на базу) (4.10, пункт 10). Эта инверсия необходима для обеспечения безопасности типов. Обратите внимание, что указатель на член не является указателем на объект или указателем на функцию, и правила преобразования таких указателей не применяются к указателям на члены. В частности, указатель на член не может быть преобразован в void *.
Короче говоря, вы можете преобразовать указатель на член доступного, не виртуального базового класса в указатель на член производного класса, если этот член не является неоднозначным.
class A {
public:
void foo();
};
class B : public A {};
class C {
public:
void bar();
};
class D {
public:
void baz();
};
class E : public A, public B, private C, public virtual D {
public:
typedef void (E::*member)();
};
class F:public E {
public:
void bam();
};
...
int main() {
E::member mbr;
mbr = &A::foo; // invalid: ambiguous; E's A or B's A?
mbr = &C::bar; // invalid: C is private
mbr = &D::baz; // invalid: D is virtual
mbr = &F::bam; // invalid: conversion isn't defined by the standard
...
Преобразование в другом направлении (через static_cast
) регулируется § 5.2.9 9:
Значение типа "указатель на член D типа cv1 T" может быть преобразовано в значение типа "указатель на член B типа cv2 T", где B - базовый класс (пункт 10 class.derived ) D, если существует допустимое стандартное преобразование из «указателя на член B типа T» в «указатель на член D типа T» ( 4.11 conv.mem ), и cv2 - это то же самое cv-квалификация, что и квалификация cv или более высокая, чем cv1 . 11) 1043 * Значение указателя нулевого элемента ( 4.11 conv.mem ) преобразуется в значение указателя нулевого элемента целевого типа. Если класс B содержит исходный член или является базовым или производным классом класса, содержащего исходный член, результирующий указатель на член указывает на исходный член. В противном случае результат приведения не определен. [Примечание: хотя класс B не должен содержать исходный член, динамический тип объекта, на который ссылается указатель на член, должен содержать исходный член; см. 5.5 expr.mptr.oper .]
11) Типы функций (включая те, которые используются в указателе на функцию-член
типы) никогда не квалифицируются как cv; см 8.3.5 dcl.fct .
Короче говоря, вы можете конвертировать из производного D::*
в базовое B::*
, если вы можете конвертировать из B::*
в D::*
, хотя вы можете использовать B::*
только для объектов, которые имеют тип D или происходят от D.