Неправильный листинг - это листинг или использование, которое является неопределенным поведением
Если я делаю приведение из базового типа в производный, но базовый тип не является экземпляром производного типа, но только использует результат, если он есть, я получаю неопределенное поведение?
Трудно понять, что я спрашиваю? взгляните на этот пример:
struct Animal { int GetType(){...} };
struct Dog : Animal { bool HasLoudBark(){...}};
struct Cat : Animal { bool HasEvilStare(){...} };
Animal * a = ...;
Dog* d = static_cast<Dog*>(a);
if(a->GetType() == DogType && d->HasLoudBark())
....
В этом случае может или не быть a
Dog
. Мы всегда делаем static_cast
of a
для Dog * d
но мы никогда не используем d
если мы не уверены в его Dog
.
Предполагая, что a
не является Dog
, это неопределенное поведение в точке броска? Или это определяется так, как мы фактически не используем d
если это действительно не Dog
?
Приветствуются ссылки на соответствующие части стандарта.
(Да, я знаю, что могу использовать dynamic_cast и RTTI, и, вероятно, это не отличный код, но меня больше интересует, действительно ли это)
Ответы
Ответ 1
Само литье имеет неопределенное поведение. Цитирование С++ 17 (n4659) [expr.static.cast] 8.2.10/11:
PRvalue типа "указатель на cv1 B
", где B
является типом класса, может быть преобразовано в prvalue типа "указатель на cv2 D
", где D
- это производный класс (раздел 13) из B
, если cv2 является такая же CV-квалификация, как или более высокая cv-квалификация, чем cv1.... Если prvalue типа "указатель на cv1 B
" указывает на B
который на самом деле является подобъектом объекта типа D
, результирующий указатель указывает на охватывающий объект типа D
В противном случае поведение не определено.
Ответ 2
Это неопределенное поведение, но если бы вы использовали reinterpret_cast
вместо static_cast
, вы бы отбросили этого демона.
[expr.reinterpret.cast]/7
Указатель объекта может быть явно преобразован в указатель объекта другого типа. Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта "указатель на cv T
", результатом является static_cast<cv T*>(static_cast<cv void*>(v))
.
Как отметил пользователь Angew, для этого требуется определенное внутреннее представление, которое гарантирует, что static_cast<void*>(d) == static_cast<void*>(a)
когда a == d
". Эти требования выполняются при работе с простым наследованием и объектами с выравниванием по умолчанию.