Я только что видел код, содержащий, например, 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 не имеют общего базового класса?





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