编译器如何知道要采用哪个catch块?

Wit*_*iko 8 c++ compiler-construction exception

假设我有以下两个文件main.cpp:

#include <iostream>

class A {};    
void foo();

int main(void)
{
    try {
        foo();
    }
    catch(const A& e) {
        std::cout << "Caught an A." << std::endl;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

并且foo.cpp:

class A {};
class B : public A {};

void foo()
{
    B b;
    throw b;
}
Run Code Online (Sandbox Code Playgroud)

现在,当我单独编译每个文件时,链接生成的目标文件,并运行生成的可执行文件,我得到预期的结果:

$ clang++ --std=c++14 -c main.cpp
$ clang++ --std=c++14 -c foo.cpp
$ clang++ --std=c++14 main.o foo.o
$ ./a.out 
Caught an A.
Run Code Online (Sandbox Code Playgroud)

那令我难以置信!类A没有虚方法.因此,它不是多态的,并且其实例在运行时不应携带任何类型信息.该main.o目标文件是不知道被扔到什么,因为实际投掷发生里面foo(),他的尸体在一个单独的编译单元定义.该foo.o目标文件提供了更多信息,但同样不知道任何catch语句和预期的类型捕获的异常的.

简而言之:我没有看到两个源文件如何单独编译然后链接可以产生上述输入,而不需要处理一些运行时类型信息.单独编译的文件都没有足够的信息来获取正确的catch块.

Chr*_*phe 5

这当然完全取决于编译器.

所有编译器的约束是:

  • 抛出时会知道异常的类型(在编译时,或者在抛出多态对象的情况下,在运行时).
  • 适用的catch块(它们可以是几个),它们的类型取决于执行路径.

这意味着必须在运行时识别类型,即使异常对象是非plymorphic.

实现此目的的一种简单方法是将指针typeinfo与抛出的对象本身一起传递给对象.这是GCC使用的方法:参见在线代码,这里是foo()为方便起见而提取的内容:

    call    __cxa_allocate_exception
    mov     edx, 0
    mov     esi, OFFSET FLAT:typeinfo for B   ; <== !! 
    mov     rdi, rax
    call    __cxa_throw
Run Code Online (Sandbox Code Playgroud)

  • @Witiko这是一个非常有趣的问题.我生成了代码制作`A`多态,认为它会使用RTTI.但不是:编译器继续传递其附加的`typeinfo`.这是因为捕获代码无法知道是否抛出了多态对象或非多态对象,因此需要统一的接口. (2认同)