Я только что видел код, содержащий 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
нет общего базового класса?
В первый раз я подумал, что этот код не будет скомпилирован, потому что 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
, но это имеет тот же эффект.
Документация для станд :: throw_with_nested утверждает, что брошенный тип будет публично извлечен из обоих станд :: nested_exception а также тип исключения, которое вы передаете. Итак, в вашем примере исключение, которое выдается концептуально, имеет тип:
class some_exception : public std::nested_exception, public std::runtine_exception
{
};
И в качестве std::runtime_exception
происходит от std_exception
Вы можете поймать это.