Spec-Zone.ru › C++

dynamic_cast преобразование

Безопасно преобразует указатели и ссылки на классы вверх, вниз и вбок по иерархии наследования.

Синтаксис

dynamic_cast< target-type >( expression )
target-type - указатель на полный тип класса, ссылка на полный тип класса или указатель на void (с необязательными квалификаторами cv)
expression - lvalue(до C++11)glvalue(начиная с C++11) полного типа класса, если target-type является ссылкой; prvalue указательного типа на полный тип класса, если target-type является указателем

Пояснение

Для удобства описания выражение «expression или результат является ссылкой на T» означает, что «это glvalue типа T», что соответствует соглашению, используемому в decltype(начиная с C++11).

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

1) Если тип expression в точности совпадает с target-type или является версией target-type с меньшим количеством квалификаторов cv, результатом является значение expression типа target-type. Иными словами, dynamic_cast можно использовать, чтобы добавить const-квалификатор. Это преобразование также можно выполнить с помощью неявного преобразования и static_cast.
2) Если target-type — «указатель на Base (с возможными квалификаторами cv)», а тип expression — «указатель на Derived (с возможными квалификаторами cv)», причем Base является базовым классом Derived, результатом будет
  • нулевое значение указателя, если expression имеет нулевое значение указателя, или
  • указатель на единственный подобъект Base объекта Derived, на который указывает expression, в противном случае. Иными словами, dynamic_cast можно использовать для приведения вверх указателей от производного класса к базовому. Это преобразование также можно выполнить с помощью неявного преобразования и static_cast.
3) Если target-type — «ссылка на Base (с возможными квалификаторами cv)», а тип expression — «Derived (с возможными квалификаторами cv)», причем Base является базовым классом Derived, результатом будет единственный подобъект Base объекта Derived, на который ссылается expression. Иными словами, dynamic_cast можно использовать для приведения вверх ссылок от производного класса к базовому. Это преобразование также можно выполнить с помощью неявного преобразования и static_cast.
4) Если expression — нулевое значение указателя полиморфного типа, результатом будет нулевое значение указателя типа target-type.
5) В противном случае expression должен быть указателем или ссылкой на объект полиморфного типа, находящийся в пределах своего времени жизни либо периода конструирования или разрушения, причем тип объекта должен быть подобен типу expression (в противном случае поведение не определено)
a) Если target-type — указатель на void (с возможными квалификаторами cv), результатом будет указатель на наиболее производный объект, на который указывает expression.
b) В противном случае выполняется проверка во время выполнения, чтобы определить, можно ли преобразовать объект, на который указывает или на который ссылается expression, в тип Target, на который указывает или на который ссылается target-type:
i) Если в наиболее производном объекте, на который указывает или на который ссылается expression, expression указывает на общедоступный подобъект базового класса объекта Target и от подобъекта, на который указывает или на который ссылается expression, производен только один объект типа Target, результат указывает на этот объект Target. Иными словами, dynamic_cast можно использовать для приведения вниз указателей и ссылок от базового класса к производному.
ii) В противном случае, если expression указывает на общедоступный подобъект базового класса наиболее производного объекта и наиболее производный тип объекта имеет однозначный и общедоступный базовый класс типа Target, результат указывает на подобъект Target наиболее производного объекта. Иными словами, dynamic_cast можно использовать для поперечного приведения указателей и ссылок между двумя типами, производными от одного базового типа.
iii) В противном случае проверка во время выполнения завершается неудачей.
  • Если target-type является типом указателя, результатом будет нулевое значение указателя типа target-type.
  • Если target-type является ссылочным типом, выбрасывается исключение типа, которое соответствовало бы обработчику типа std::bad_cast.

Если dynamic_cast используется в конструкторе или деструкторе (непосредственно или косвенно), а expression ссылается на объект, который в данный момент конструируется или разрушается, этот объект считается наиболее производным. Если target-type не является указателем или ссылкой на собственный класс конструктора или деструктора либо на один из его базовых классов, поведение не определено.

Как и в случае с другими выражениями приведения, результат является:

  • lvalue, если target-type является ссылочным типом
  • rvalue, если target-type является типом указателя
(до C++11)
  • lvalue, если target-type является типом lvalue-ссылки (expression должен быть lvalue)
  • xvalue, если target-type является типом rvalue-ссылки (expression может быть lvalue или rvalue(до C++17)должен быть glvalue (prvalue материализуются)(начиная с C++17) полного типа класса)
  • prvalue, если target-type является типом указателя
(начиная с C++11)

Примечания

Приведение вниз также можно выполнить с помощью static_cast, избежав затрат на проверку во время выполнения, но оно безопасно только в том случае, если программа может гарантировать (с помощью другой логики), что объект, на который указывает expression, определенно является Derived.

Некоторые формы dynamic_cast используют идентификацию типов во время выполнения (RTTI), то есть сведения о каждом полиморфном классе в скомпилированной программе. В компиляторах обычно есть параметры, отключающие включение этих сведений.

Ключевые слова

dynamic_cast

Пример

#include <iostream>

struct V
{
    virtual void f() {} // must be polymorphic to use runtime-checked dynamic_cast
};

struct A : virtual V {};

struct B : virtual V
{
    B(V* v, A* a)
    {
        // casts during construction (see the call in the constructor of D below)
        dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
        dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
    }
};

struct D : A, B
{
    D() : B(static_cast<A*>(this), this) {}
};

struct Base
{
    virtual ~Base() {}
};

struct Derived : Base
{
    virtual void name() {}
};

int main()
{
    D d; // the most derived object
    A& a = d; // upcast, dynamic_cast may be used, but unnecessary
    
    [[maybe_unused]]
    D& new_d = dynamic_cast<D&>(a); // downcast
    [[maybe_unused]]
    B& new_b = dynamic_cast<B&>(a); // sidecast
    
    Base* b1 = new Base;
    if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
    {
        std::cout << "downcast from b1 to d successful\n";
        d->name(); // safe to call
    }
    
    Base* b2 = new Derived;
    if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
    {
        std::cout << "downcast from b2 to d successful\n";
        d->name(); // safe to call
    }
    
    delete b1;
    delete b2;
}

Вывод:

downcast from b2 to d successful

Отчеты об ошибках

Следующие отчеты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.

DR Применяется к Опубликованное поведение Правильное поведение
CWG 1269 C++11 проверка во время выполнения не выполнялась для xvalue
expression ов, если target-type является типом rvalue-ссылки
выполнялась
CWG 2861 C++98 expression мог указывать на объект или ссылаться на объект, тип которого недоступен в этом случае поведение не определено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
    • 7.6.1.7 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++20 (ISO/IEC 14882:2020):
    • 7.6.1.6 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++17 (ISO/IEC 14882:2017):
    • 8.2.7 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++14 (ISO/IEC 14882:2014):
    • 5.2.7 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++11 (ISO/IEC 14882:2011):
    • 5.2.7 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++98 (ISO/IEC 14882:1998):
    • 5.2.7 Динамическое приведение [expr.dynamic.cast]
  • Стандарт C++03 (ISO/IEC 14882:2003):
    • 5.2.7 Динамическое приведение [expr.dynamic.cast]

См. также

  • const_cast
  • static_cast
  • reinterpret_cast
  • явное приведение
  • неявные преобразования

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/dynamic_cast

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API