2

我偶然注意到了以下行为(通过引用错过了捕获),但我找不到如果我事先知道的话,可以让我预测它的信息。

用最小的例子

#include <iostream>
#include <stdexcept>


int main()
{
    try
    {
        // Added this and the next line to check that the exception 
        // created has already the what() set to the input string.
        std::out_of_range d("Out of range exception"); 
        std::cout << "d.what() = " << d.what() << std::endl;
        throw d;
    }
    catch (std::exception e) // Captured by value
    {
        std::cout << e.what() << std::endl;
    }

}

如果我g++ -std=c++17用 Visual C++ 编译它,我会得到不同的行为。第一个打印d.what() = Out of range exception\nstd::exception,第二个打印d.what() = Out of range exception\nOut of range exception

std::out_of_range原则上,当按值捕获并转换为std::exception类型时,可能会有切片。这意味着我可以期望std::out_of_range在打印对象时不会得到与对象相同的行为what()

问题:我不知道如何解释的部分是两个编译器的不同行为。这是因为这种切片在 C++ 标准化中是未定义的行为,还是这两个编译器之一不符合它?

额外的观察:我只是注意到在这个链接中没有提到这个类std::exception有一个输入 a 的构造函数const char* const &,而在微软网站上它们包括它。我偶然的例子表明他们确实以不同的方式实现了这些类。我的问题仍然是他们是否被允许(如果这种行为是未定义的),或者他们中的一个不遵守,哪一个。

4

1 回答 1

1

对象仍在被切片;您可以使用typeid( e ).name()打印出实际类型,它显示为std::exception. 正如您所发现的,MSVC 实现what()返回指向在构造时设置的字符串的指针,因此当异常被切回到基本异常std::exception时它不会丢失。out_of_range

根据https://en.cppreference.com/w/cpp/error/exception/exception,what ()“返回一个实现定义的字符串”,因此 MSVC 可以自由地这样做。

要打印类型,请将其添加到您的 catch 块中:

std::cout << "e.what() = " << e.what() << " 实际类型 = " << typeid( e ).name() << std::endl;

于 2019-03-08T19:22:06.100 回答