Как можно выполнить 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 нет общего базового класса?

2 ответа

Решение

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

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

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
};

ожидаемый dynamic_cast сделает статическую проверку наследования, но я ошибся

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

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

Хотя я не смог найти соответствующую стандартную спецификацию, которая явно упоминает, что dynamic_cast позволяет это

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

  • 5) Если выражение является указателем или ссылкой на полиморфный тип Base, а new_type является указателем или ссылкой на тип, полученный, выполняется проверка во время выполнения:

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

    • b) В противном случае, если выражение указывает / относится к общедоступной базе наиболее производного объекта и, одновременно, наиболее производный объект имеет однозначный общедоступный базовый класс типа Derived, результат приведения точек / относится к этому производному (This известен как "Sidecast".)

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


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

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

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


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

Документация для std::throw_with_nested гласит, что тип throw будет публично извлечен как из std:: nested_exception, так и из типа исключения, которое вы передаете. Таким образом, в вашем примере, исключение, которое выдается концептуально, имеет тип:

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

};

И в качестве std::runtime_exception происходит от std_exception Вы можете поймать это.

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