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

3

Решение

В первый раз я подумал, что этот код не будет скомпилирован, потому что 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, но это имеет тот же эффект.

4

Другие решения

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

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

};

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

1

По вопросам рекламы [email protected]