在带有msvc11的静态类析构函数中使用std::system_category()
Using std::system_category() in static class destructor with msvc11
我对C++还很陌生,但在向Microsoft报告错误之前,我想确保我在这里没有做错什么。
以下是一些示例代码:
#include <system_error>
using namespace std;
class Test
{
public:
~Test()
{
throw system_error(5, system_category());
}
};
Test test;
void testfunc()
{
throw system_error(5, system_category());
}
void main()
{
try
{
testfunc();
}
catch ( const system_error& e)
{
}
}
现在,我希望Windows说"运行时以一种意想不到的方式请求程序退出"。然而,我得到了一个名为"纯虚拟函数"的错误。经过一点调试,我注意到当静态类析构函数获得std::system_category
引用时,::name
和::message
成员是纯虚拟的。然而,当它在testfunc()
中构造时,那些vtable指针指向有效函数。
我的问题是,以这种方式构造system_error
异常是不是做错了什么?我有一些代码,基本上是在做throw system_error(GetLastError(), system_category());
。这恰好是在静态析构函数中执行的,我得到了一个名为error的纯虚拟函数。
要从Windows的GetLastError()
函数抛出异常,我应该以不同的方式构造异常吗?还是这是msvc11的C++运行时中的一个错误?
编辑
我的问题有点混乱。我的实际代码比这个例子更复杂,实际上我没想到我的一个析构函数会抛出。我的析构函数必须调用一个可能引发的函数。如果我将代码更改为:
~Test()
{
try
{
callSomeFuncThatCouldThrow();
}
catch ( … ) { }
}
我将仍然得到纯虚拟函数调用错误。这是因为当构造system_error(在callSOmeFuncThatCouldThrow()
中)时,它试图使用我给它的system_category
的::message
成员,这会导致错误。
看起来像是一个Microsoft错误。std::error_category
是各种未命名类型的抽象基类,其中一个是system_category()
返回的类型。有一个该类型的对象,所有对system_category()
的调用都返回对该对象的引用。您看到的情况看起来像是在test
对象的析构函数运行之前,该对象正在被销毁。如果你想满足纯粹主义者,把你的析构函数改为:
Test::~Test() {
const std::error_category& cat = std::system_category();
std::cout << cat.name() << 'n';
}
这是Visual C++标准库实现中的一个错误。全局错误类别对象是使用类模板的静态数据成员实现的。不幸的是,在单个翻译单元(也称为源文件)中,这些数据成员将在翻译单元中的所有其他命名空间范围对象之后初始化,并在这些对象之前销毁(因为销毁的顺序与初始化相反)。
我的建议是避免在初始化和终止期间(即,在初始化所有静态对象之前或在开始销毁静态对象之后)调用generic_category()
、iostream_category()
或system_category()
。
如果无法做到这一点,则以下解决方法可能有效。它在我运行的几个简单测试中都有效,但我不能保证它在所有情况下的行为(这不是一个"官方"的解决方法;它只是一个潜在的解决方法)。将.cpp文件添加到您的项目中,其中包含以下内容:
// This is a workaround for a bug in the Visual C++ 2012 implementation of the
// global error category objects--generic_category(), iostream_category(), and
// system_category().
#ifdef _MSC_VER
#if _MSC_VER != 1700
# error Please verify that this fix is still required and is still correct!
#endif
#include <system_error>
// Ensure that static objects in this translation unit get initialized "first":
#pragma warning(suppress: 4073)
#pragma init_seg(lib)
// Explicitly instantiate the global error objects in this translation unit:
template struct std::_Error_objects<int>;
#endif // _MSC_VER
#pragma init_seg(lib)
应确保_Error_objects<int>
的静态数据成员将在用户代码中的任何静态对象之前初始化。不要在此源文件中放入任何其他内容:#pragma init_seg
应用于转换单元中的所有对象。_Error_objects
是一个实现细节,该实现细节可能随时更改,因此编译器会进行版本检查。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- 使用 (cin) 用户输入将其粘贴到 std::system 中,并在另一个终端中运行带有输入的命令
- 如何在 std::p air 中使用 System::Guid 和自定义枚举?
- 通过std::system调用python时出错
- std::system 实例化单一实例对象时的异常
- 如何将C std ::未来返回值调整到C#System.Threading.tasks.task
- 如何在Windows上使用std::system运行带有空格的可执行文件
- std::system() 无法正常工作
- VC++ std::system() API 在调用 CMD.exe 后立即返回
- 使用 std::system 将 bash(例如 ssh)转换为 C++
- 我应该使用std::system来编写单元测试的脚本部分吗
- 在c++中简单地将System::String转换为std::String
- 当用std::system启动进程时,我可以指定一个工作目录吗
- C++:为什么 std::system( "exit 1" ) 返回 256?
- System::Convert::ToString()与std::to_string()的区别