Динамическое снижение частного наследования в частной области - PullRequest
18 голосов
/ 03 августа 2011

Настройка этого вопроса , с которым я столкнулся. Рассмотрим:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

Поскольку aPtr1 на самом деле относится к типу B*, и поскольку мы получили полный доступ к B и его наследованию от A, я ожидал, что оба приведения будут работать. Но они этого не делают; Зачем? Есть ли другой способ добиться этого броска?

Обратите внимание, что:

  • Если бы foo() не был членом B, оба приведения не состоялись бы.
  • Если B публично наследуется от A, оба приведения будут работать.

Ответы [ 2 ]

15 голосов
/ 03 августа 2011

5.2.7 (ИСО / МЭК 14882, 29.12.2003) довольно ясно говорит об этом:

[о выражении dynamic_cast<T>(v)]

Если T - это «указатель на cv1 B», а v имеет тип «указатель на cv2 D», так что B является базовым классом Dрезультат - указатель на уникальный подобъект B объекта D, на который указывает v.[... бла бла о cv1 и cv2 ...] и B должны быть доступным однозначным базовым классом D (выделено мной)

(вспомните 11.2 «Базовый класс считается доступным, если доступен изобретенный публичный член базового класса.» ).

Это объясняет, почему работает первое приведение.Теперь для второго:

[...]

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

Проверка времени выполнения логически выполняется следующим образом:

  • Если в наиболее производном объекте, указанном (указанном),на v, v указывает (ссылается) на подобъект базового класса public объекта T, и если только один объект типа T получен из подобъекта, на который указывает (ссылается) v, результатом является указатель (ссылка на lvalue) на этот T объект.
  • В противном случае, если v указывает (ссылается) на подобъект базового класса public самого производного объектаи тип самого производного объекта имеет базовый класс типа T, то есть однозначный и public, результатом является указатель (ссылка на lvalue) на подпрограмму T-объект самого производного объекта.
  • В противном случае проверка во время выполнения завершится неудачно.

значение неудачного приведения к типу указателя является нулевым значением указателя требуемого типа результата.Неудачное приведение к ссылочному типу выдает bad_cast (18.5.2).

Таким образом, похоже, что наблюдаемое вами поведение связано с наследованием private: даже если базовый класс доступен, онне public , а стандарт требует public , недоступен.

Раздражает, не так ли?У меня нет под рукой черновика C ++ 0x, возможно, кто-то может отредактировать мой ответ цитатами из него, если что-то изменилось.

Есть ли другой способ добиться этого броска?

Это зависит от того, что вы хотите сделать.По сути, частное наследование - это просто еще одно устройство для выполнения композиции.Если вы действительно хотите вернуть указатель на частный производный экземпляр, то либо сделайте наследование общедоступным, либо верните член.

В любом случае, вы будете рады узнать, что static_cast, похоже, не имеетэто ограничение:

5.2.9.[about static_cast<T>(v)] [...]

Значение типа «указатель на cv1 B», где B - тип класса, может быть преобразовано в значение типа «указатель на cv2 D»,где D является классом, полученным (пункт 10) из B, , если существует допустимое стандартное преобразование из «указателя на D» в «указатель на B» (4.10), cv2 является той же квалификацией cv, что и,или cv-квалификация больше, чем, cv1 и B не является виртуальным базовым классом D. Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя типа назначения.Если значение типа «указатель на cv1 B» указывает на B, который на самом деле является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае результат приведения не определен.

, поэтому, если вы точно знаете, каков действительный динамический тип указателя, вы можете static_cast внутри foo.

.любая дополнительная информация о , почему существует такое несоответствие.

1 голос
/ 03 августа 2011

Они не работают, потому что в A. нет виртуальных функций. Когда вы выполняете downcast, это тривиально - компилятор, вероятно, даже не удосуживается выполнить проверку. Когда вы делаете upcast, тогда компилятор должен проверить, но он определен для работы только тогда, когда у вас есть виртуальные функции. Если вы этого не сделаете, то компилятор не сможет выполнить проверку, и результат будет NULL.

Уровень защиты наследования и другие проблемы доступности ортогональны этой проблеме, и они существуют только во время компиляции, если программа компилируется, то они работают нормально.

Обратите внимание, что:

Если бы foo () не был членом B, оба приведения не состоялись бы.

Если B наследует Публично, оба приведения будут работать.

Это просто неправда. foo() не имеет абсолютно никакого отношения к функциональности RTTI - он не виртуален и даже не является членом экземпляра. Если B наследуется от A публично, то у A все еще нет виртуальных функций, и он все равно не будет работать.

...