Как можно использовать dynamic_cast из std :: exception в std :: nested_exception?

Я только что видел код, содержащий, например, dynamic_cast от std::exception до std::nested_exception,

try {
    std::throw_with_nested(std::runtime_error("error"));
} catch (std::exception &e) {
    auto &nested = dynamic_cast<std::nested_exception&>(e);
    std::cout << "ok" << std::endl;
}

В первый раз я подумал, что этот код не будет скомпилирован, потому что std::nested_exception - это не производный от std::exception, и я ожидал, что dynamic_cast выполнит статическую проверку наследования, но я ошибался.

Хотя мне не удалось найти соответствующую стандартную спецификацию, в которой явно упоминается, что dynamic_cast позволяет это, я подтвердил, что все три основных компилятора (clang / gcc / msvc) разрешают dynamic_cast между совершенно несвязанными типами.

Но все же std::nested_exception не является производным от std::exception, поэтому я подумал, что dynamic_cast выдаст исключение bad_alloc, а "ok" никогда не распечатывается. Я снова ошибся.

Теперь мне интересно, как это может работать. Это что-то особенное и исключительное для std::exception и std::nested_exception? Или я могу сделать еще один успешный dynamic_cast<A&>(b), где тип A и тип объекта b не имеют общего базового класса?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
530
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

At the very first time, I thought this code won't be compiled because std::nested_exception is not derived from std::exception

Этого недостаточно - std::nested_exception предназначен для использования в качестве класса миксинов, например

struct MyExceptionWrapper: public std::exception, std::nested_exception
{
    // an 3rd-party component of my library threw
    // and I want to wrap it with a common interface
};

expected dynamic_cast would do static check of inheritance but I was wrong

В приведенном выше случае dynamic_cast должен проверить во время выполнения, является ли ваш std::exceptionВ самом делеMyExceptionWrapper, и в этом случае это это такжеstd::nested_exception.

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

Although I couldn't find related standard specification which explicitly mentions that dynamic_cast allows this

Это все хорошо задокументированы. Мы обсуждаем следующий пункт со связанной страницы:

  • 5) If expression is a pointer or reference to a polymorphic type Base, and new_type is a pointer or reference to the type Derived a run-time check is performed:

(обратите внимание, что Base=std::exceptionявляется полиморфен)

    • b) Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".)

Поскольку вы не можете сказать во время компиляции, что std::exception& на самом деле не является MyExceptionWrapper, вы должны сделать это во время выполнения.


PS. если вы хотите избежать случайного переброса внутрь блока catch, просто напишите

auto *nested = dynamic_cast<std::nested_exception*>(&e);

вместо. Затем вы можете проверить наличие nullptr, чтобы узнать, удалось ли это сделать.


PPS. Как предполагает Шон, приведенный выше MyExceptionWrapper, скорее всего, будет типом, сгенерированным throw_with_nested, но он имеет тот же эффект.

Это второстепенный вопрос, а не унижение.

T.C. 25.04.2018 22:00

Вы совершенно правы, я вставил не ту пулю. Спасибо!

Useless 25.04.2018 22:57

В документации для std :: throw_with_nested указано, что выброшенный тип будет публично извлекать из std :: nested_exceptionа также тип исключения, которое вы передаете. Итак, в вашем примере возникшее исключение концептуально имеет тип:

class some_exception : public std::nested_exception, public std::runtine_exception
{

};

И поскольку std::runtime_exception является производным от std_exception, вы можете его поймать.

Другие вопросы по теме