3

私は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());ました。これはたまたま静的デストラクタで実行され、エラーと呼ばれる純粋仮想関数を取得しました。

WindowsのGetLastError()関数から例外をスローするには、例外を別の方法で作成する必要がありますか、それともmsvc11のC ++ランタイムのバグですか?

編集

私の質問について少し混乱がありました。私の実際のコードはこの例よりも複雑であり、実際には、デストラクタの1つがスローされるとは思っていませんでした。私のデストラクタは、スローする可能性のある関数を呼び出す必要があります。コードを次のように変更した場合:

~Test()
{
    try
    {
        callSomeFuncThatCouldThrow();
    }
    catch ( … ) { }
}

I will still get the pure virtual function call error. This is because when the system_error is constructed (in callSOmeFuncThatCouldThrow()) it tries to use the ::message member of the system_category I'm giving it, which causes the error.

4

2 に答える 2

4

Looks like a Microsoft bug. std::error_category is an abstract base class for various unnamed types, one of which is the type returned by system_category(). There's one object of that type, and all calls to system_category() return a reference to that object. What you're seeing looks like that object is being destroyed before the destructor of the test object runs. If you want to satisfy the purists, change your destructor to:

Test::~Test() {
    const std::error_category& cat = std::system_category();
    std::cout << cat.name() << '\n';
}
于 2012-09-28T20:07:09.523 に答える
3

これは、VisualC++標準ライブラリの実装のバグです。グローバルエラーカテゴリオブジェクトは、クラステンプレートの静的データメンバーを使用して実装されます。残念ながら、単一の変換ユニット(別名ソースファイル)内では、これらのデータメンバーは、変換ユニット内の他のすべての名前空間スコープオブジェクトの後に初期化され、それらのオブジェクトのに破棄されます(初期化の逆の順序で破棄が発生するため)。

、、または初期化と終了中(つまり、すべての静的オブジェクトが初期化される前、または静的オブジェクトの破棄が開始された後)の呼び出し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は実装の詳細であり、その実装の詳細はいつでも変更される可能性があるため、コンパイラのバージョンがチェックされます。

于 2012-11-16T22:54:08.680 に答える