Почему dynamic_cast'ing между указателями из разных иерархий типов хорошо определен? - PullRequest
0 голосов
/ 22 января 2020

Это дополнительный вопрос к: Почему dynamic_cast разрешено давать нулевой указатель для классов polymorphi c, когда целевой указатель не относится к типу базового класса? .

Стандарт C ++ 17 определяет dynamic_cast в §8.2.7 [expr.dynami c .cast] .

§8.2.7 (1) состояния

Результатом выражения dynamic_cast<T>(v) является результат преобразования выражения v в тип T. T должен быть указателем или ссылкой на полный тип класса, или «указатель на cv void». [...]

(2) определяет категории значений, (3) преобразование в cv для одного и того же типа и (4) случай nullptr. Каждый второй абзац [expr.dynami c .cast] связан с полиморфными c типами в одной иерархии типов.

Я не знаю ни одного варианта использования для dynamic_cast кроме приведения между типами внутри одной иерархии типов. С учетом следующих типов:

struct A
{
    virtual ~A() = default;
};

struct B : A
{
    virtual ~B() = default;
};

A* можно привести к B*, что, вероятно, является наиболее распространенным вариантом использования для dynamic_cast:

A* ptr = new B;
dynamic_cast<B*>(ptr);

Я ожидаю этого компилировать, потому что B и A находятся в одной иерархии типов (B является производным от A). В хорошо определенной программе указатель типа A* может указывать на объект типа B.

Теперь давайте удалим базовый класс:

struct C
{
    virtual ~C() = default;
};

struct D
{
    virtual ~D() = default;
};

Удивительно, но dynamic_cast все еще разрешено:

C* ptr;
dynamic_cast<D*>(ptr);

Я не вижу никакой причины, по которой такое приведение не могло быть неправильно сформировано (что приводило к ошибке компилятора). Насколько я знаю, в хорошо определенной программе нет никакого способа, которым ptr может указывать на объект типа D.

Почему разрешен такой бросок? Почему это не запрещено стандартом?

1 Ответ

7 голосов
/ 22 января 2020

Приведение от C* до D* не может быть отклонено компилятором, поскольку dynamic_cast может приводить не только "вверх" и "вниз" иерархию классов, но и "вбок". Например, предположим, что у нас есть

struct E : C, D { };
C* p = new E;
auto q = dynamic_cast<D*>(p);

Тогда q будет указывать на подобъект D полного объекта E, содержащего объект C, на который указывает p.

Это указано в [expr.dynami c .cast] / (8.2) .

Конечно, достаточно умный компилятор все же может предупредить вас, в некоторых случаях, что dynamic_cast гарантированно потерпит неудачу (если он знает, откуда берется указатель).

...