Указатель на преобразование члена - PullRequest
14 голосов
/ 28 ноября 2010

Я только что нашел следующие абзацы в стандартном черновике c ++ 03, относящиеся к указателю на преобразование члена.

4.11 / 2 Указатель на преобразование члена

Значение типа «указатель на член B типа cv T», где B - тип класса, может быть преобразовано в значение типа «указатель на член D типа cv T», где D - это тип производный класс (пункт 10) из B. Если B является недоступным (пункт 11), неоднозначным (10.2) или виртуальным (10.1) базовым классом D, программа, которая требует этого преобразования, является неадекватной. Результат преобразования ссылается на тот же член, что и указатель на член до преобразования, но ссылается на член базового класса, как если бы он был членом производный класс. Результат ссылается на член в экземпляре D из B. Поскольку результат имеет тип «указатель на член D типа cv T», его можно разыменовать с помощью объекта D. Результат такой же, как если бы указатель на член B был разыменован с подобъектом B объекта D. Значение указателя с нулевым элементом преобразуется в значение указателя с нулевым элементом типа назначения 52)

5.2.9 / 9 static_cast

Значение типа «указатель на член D типа cv1 T» может быть преобразовано в значение типа «указатель на член B типа cv2 T», где B - базовый класс (пункт 10) из D , если существует допустимое стандартное преобразование из «указателя на члена B типа T» в «указатель на члена D типа T» (4.11), и cv2 является той же квалификацией cv, что и, или большей квалификацией cv, чем, cv1.63) Значение указателя нулевого элемента (4.11) преобразуется в значение указателя нулевого элемента целевого типа. Если класс B содержит исходный член или является базовым или производным классом класса, содержащего исходный член, результирующий указатель на член указывает на исходный член. В противном случае результат приведения не определен. [Примечание: хотя класс B нужен не содержать исходный член, динамический тип объекта, на который разыменовывается указатель на член, должен содержать исходный член; см. 5.5. ]

Так вот мой вопрос. Как говорится в 5.2.9 / 9, указатель на член D может быть преобразован в указатель на член B, если существует допустимое преобразование, описанное в 4.11 / 2. Означает ли это, что если есть член 'm' из D, который не унаследован от B, указатель на член 'm' не может быть приведен к типу указателя на член B?

class Base { };
class Derived : public Base 
{
    int a;
};
typedef int Base::* BaseMemPtr;
BaseMemPtr pa = static_cast<BaseMemPtr>(&Derived::a); // invalid, as per 5.2.9/9 ?

В примечании к 5.2.9 / 9 также говорится, что хотя класс B не должен содержать исходный член, динамический тип объекта, на который ссылается указатель на член, должен содержать исходный член.

Я путаюсь с формулировкой параграфа. Код выше действителен?

Я искал на сайте, и есть похожий вопрос, c ++ наследование и указатели на функции-члены , ответ которого охватывал только случай преобразования указателя на член базового класса в указатель на член производного класса.

1 Ответ

11 голосов
/ 28 ноября 2010

Код, который вы написали, абсолютно действителен. В этом нет ничего плохого (кроме того, что Derived::a является частным). Это хорошо сформировано, и поведение определено (пока). Как сказано в цитируемой части стандарта, вполне законно указывать на членские указатели, используя явный static_cast, что именно то, что вы делаете. 5.2.9 / 9 никогда не говорит, что указанный член должен присутствовать в базовом классе.

Также, как вы правильно цитировали из стандарта, наличие фактического члена в объекте требуется позже в момент разыменования указателя, а не в данный момент инициализации. Это, конечно, зависит от динамического типа объекта, используемого слева от оператора доступа к элементу (->* или .*). Тип известен только во время выполнения и поэтому не может быть проверен компилятором.

Это требование включено в качестве простого примечания в 5.2.9 / 9, но оно повторяется в более официальной форме в 5.5 / 4

.

4 Если динамический тип объекта не содержит члена, которому указатель ссылается, поведение не определено.

Так, например, в контексте вашего примера следующие строки кода правильно сформированы

Base b;
b.*pa; // 1

Derived d;
d.*pa; // 2

Base *pb = &d;
pb->*pa; // 3

Однако первая разыменование приводит к неопределенному поведению (поскольку объект b не содержит члена), в то время как и вторая, и третья совершенно допустимы.

...