Подтвердить что ты не робот

Динамическое понижение на частном наследовании в частной сфере

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

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 публично, оба броска будут работать.
4b9b3361

Ответ 1

5.2.7 (ISO/IEC 14882, 12/29/2003) довольно четко сказано:

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

Если T является "указателем на cv1 B" и v имеет тип "указатель на cv2 D", так что B является базовым классом D, результатом является указатель на уникальный объект B объекта D, на который указывает v. [... bla bla о cv1 и cv2...] и B должен быть доступным однозначным базовым классом of 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: даже если базовый класс доступен, он не является общедоступным, а стандарт требует публичного доступа, недоступного.

Раздражает, не так ли? У меня нет подходящего проекта С++ 0x, возможно, кто-то может отредактировать мой ответ с помощью кавычек из него, если все изменилось.

Есть ли другой способ добиться этого приведения?

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

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

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

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

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

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

Ответ 2

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

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

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

Если foo() не были членом B, оба броска не сработают.

Если B наследует из публично, оба броска будут работать.

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