Как можно выполнить 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
Вы можете поймать это.