如何从 std::exception 到 std::nested_exception dynamic_cast?

How one can dynamic_cast from std::exception to std::nested_exception?

本文关键字:std exception dynamic cast nested      更新时间:2023-10-16

例如,我刚刚看到一个包含从std::exceptionstd::nested_exceptiondynamic_cast的代码,

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::exception派生std::nested_exception我希望dynamic_cast会对继承进行静态检查,但我错了。

虽然我找不到相关的标准规范明确提到dynamic_cast允许这样做,但我确认所有三个主要的编译器(clang/gcc/msvc)都允许在完全不相关的类型之间进行dynamic_cast

但是,std::nested_exception不是从std::exception派生的,所以我认为dynamic_cast会抛出一个bad_alloc异常,"ok"从未打印过。我又错了。

现在,我想知道这如何工作。这对std::exceptionstd::nested_exception来说是一种特殊和特殊的东西吗?或者,我是否可以在对象b类型A和类型没有公共基类的情况下成功dynamic_cast<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) 否则,如果表达式指向/引用派生最多的对象的公共基,并且同时,派生
    • 最多的对象具有派生类型的明确公共基类,则强制转换点的结果/引用该派生(这称为"侧播")。

由于您无法在编译时判断std::exception&不是真正的MyExceptionWrapper,因此您必须在运行时执行此操作。


如果你想避免在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衍生出来的,因此您可以抓住它。