在静态类析构函数中使用带有msvc11的std :: system_category()

时间:2012-09-28 19:26:50

标签: c++ visual-c++ c++11 visual-studio-2012 system-error

我对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()中)时,它会尝试使用我::message的{​​{1}}成员,这会导致错误。

2 个答案:

答案 0 :(得分:4)

看起来像微软的错误。 std::error_category是各种未命名类型的抽象基类,其中一个是system_category()返回的类型。有一个这种类型的对象,所有对system_category()的调用都返回对该对象的引用。您所看到的内容似乎是在test对象的析构函数运行之前该对象被销毁。如果您想满足纯粹主义者,请将您的析构函数更改为:

Test::~Test() {
    const std::error_category& cat = std::system_category();
    std::cout << cat.name() << '\n';
}

答案 1 :(得分:3)

这是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是一个实现细节,实现细节可能随时更改,因此编译器版本会检查。