我从这里开始运行MWE: http://www.cplusplus.com/reference/ios/ios/exceptions/ 在我的机器上它没有捕获异常。这是我的代码
#include <iostream>
#include <fstream>
int main()
{
std::ifstream file;
file.exceptions( std::ifstream::failbit | std::ifstream::badbit );
try
{
file.open("IDoNotExist.txt");
}
catch(const std::ifstream::failure& e)
{
std::cout << "Bad luck!" << std::endl;
}
}
在Arch-Linux上使用gcc 6.2.1
我得到:
在抛出'std :: ios_base :: failure'的实例后终止调用
what():basic_ios :: clear
但是,在上面发布的链接中,提到代码还应该捕获与打开文件相关的异常。出了什么问题?
答案 0 :(得分:4)
问题在于,随着对C ++ 11 ABI的更改,许多类在libstdc++6.so
中重复,一个版本使用旧版ABI,另一个版本使用新版本。
异常类没有重复,所以当时不存在这个问题。但是,在该语言的一些较新版本中,我们决定std::ios_base::failure
应该来自std::system_error
而不是std::exception
......但system_error
是C ++ 11只有类,所以它必须使用新的ABI标志或它会抱怨。现在你有两个不同的std::ios_base::failure
课程并且手中一团糟!
简单的解决方案是使用-D_GLIBCXX_USE_CXX11_ABI=0
编译您的程序并辞退到旧的ABI,直到错误得到解决。或者,写catch (std::exception &e)
。
答案 1 :(得分:2)
N.B。 std::ifstream::failure
是std::ios_base
ifstream
基类中定义的类型,因此本答案的其余部分将其称为std::ios_base::failure
或仅std::ios::failure
。
这里的问题是,自GCC 5以来,libstdc ++中有两个不同的std::ios_base::failure
定义(更多详细信息,请参阅Dual ABI docs)。这是必要的,因为C ++ 11改变了ios::failure
的定义:
class ios_base::failure : public exception {
为:
class ios_base::failure : public system_error {
这是ABI更改,因为system_error
与exception
相比有额外的数据成员,因此会更改ios::failure
的大小和布局。
因此,自GCC 5.1起,当您的代码名称为std::ios_base::failure
(或std::ifstream::failure
或其他任何名称)时,您获得的定义取决于_GLIBCXX_USE_CXX11_ABI
宏的值。当libstdc++.so
库中发生iostream错误时,抛出哪种类型取决于构建libstdc++.so
时宏的值。如果您尝试捕获一种类型并且库抛出另一种类型,则catch将不起作用。这就是你所看到的。在你的代码std::ifstream::failure
中命名新类型,但是库抛出旧类型,这是一个不同的类,因此catch处理程序不匹配。
使用GCC 5.x和6.x,libstdc++.so
中的代码会抛出旧类型,因此要捕获它,您需要使用-D_GLIBCXX_USE_CXX11_ABI=0
编译代码或将处理程序更改为{{1 (因为新旧类型都来自catch (const std::exception&)
)。
使用GCC 7.x,库中的代码会抛出 new 类型(由PR 66145更改),因此您需要使用std::exception
或catch {编译代码{1}}。
对于GCC 8.x,库现在抛出一个异常类型,可以由旧类型的处理程序捕获或新类型(由PR 85222更改),所以你不要我不需要改变你的代码。它将Just Work™。