3

私は非常に簡単な解決策を書きましたが、誰かが笑って、ここに示されているように欠陥を見つけましたhttp://ideone.com/IcWMEf

#include <iostream>
#include <ostream>
#include <functional>
#include <exception>
using namespace std;

// Wrong scope(failure)
class FailBlockT
{
    typedef function<void()> T;
    public:
    T t;
    FailBlockT(T t)
    {
        this->t=t;
    }
    ~FailBlockT()
    {
        if (std::uncaught_exception())
        {
            t();
        }
    }
};

struct Test
{
    ~Test()
    {
        try
        {
            FailBlockT f([]()
            {
                cout << "failure" << endl;
            });
            // there is no any exception here, but "failure" is printed.
            // See output below
        }
        catch(...)
        {
            cout << "some exception" << endl;
        }
    }
};

int main()
{
    try
    {
        Test t;
        throw 1;
    }
    catch(int){}
    return 0;
}

要するに、問題は私のコードがを見るということですstd::uncaught_exception()。例外がスローされ、通常のデストラクタが実行された場合。そこでスコープの失敗を使用するstd::uncaught_exception()と、単にスコープから外れるのではなく、例外が原因でオブジェクトのスコープが失われたと見なされます。

通常はスコープを離れるのと、例外がスローされるのとを区別するための良い解決策は考えられません。はい、私はdtorsでスローするのは悪い考えだと知っていますが、例外をスローすることは決してないので、この問題に気付かないのはそのためです。

これを区別/解決するにはどうすればよいですか?

4

2 に答える 2

4

例外はスローされませんでしたが、スローされたと考えられます。

例外スローされましたが、そこからではありません

C ++ 11には、「例外は私のすぐ下のコードからスローされましたが、コールスタックの他の場所のコードからはスローされませんでしたか?」と尋ねるメカニズムはありません。std::uncaught_exceptionは、本来の機能を正確に実行しています。関数が呼び出されたときに、現在解決中の例外があるかどうかを確認します。そしてあるので、それは戻りますtrue

C ++ 17はstd::uncaught_exceptions、違いを検出するために使用できる(複数形に注意してください)を追加します。このようなツールを使用すると、FailBlockオブジェクトを機能させることができます。

template<typename Func>
class FailBlockT
{
private:
    int e_count_;
    T t_;

public:
    FailBlockT(T t) : e_count_(std::uncaught_exceptions()), t_(t) {}

    FailBlock(const FailBlock &) = delete; //The type should not be mobile.

    ~FailBlockT()
    {
        if (std::uncaught_exceptions() != e_count_)
        {
            t_();
        }
    }
};

std::uncaught_exceptions()呼び出しが行われたときにスタックの巻き戻しを引き起こしている例外の数を返します。オブジェクトのコンストラクタとデストラクタで番号が同じである場合(スタックオブジェクトであると想定)、このタイプが使用された場所で例外がスローされるため、デストラクタは呼び出されません。

しかし、このツールがなければ、例外の巻き戻しが起こっているときにスコープを終了するのではなく、スコープの終了を引き起こす例外を区別するためにできることはあまりありません。したがって、他の人と同じように、弾丸を噛んで例外をキャッチする必要があります。

FailBlockまたは、これをデストラクタに入れないでください。それらは実際にスローできる通常の関数に直接入る必要があるように思えます(そしてデストラクタは決してスローしないでください)。意味のないコーナーケースが気になっているようです。

于 2012-10-21T21:16:57.103 に答える
4

通常はスコープを離れるVSと例外がスローされることを区別するための良い解決策は考えられません。

stack_unwindingライブラリを確認します-C++でscope(failure)およびscope(success)機能を実装しました。

これは、プラットフォーム固有の関数uncaught_exception_countに基づいています。これは、標準ライブラリのstd :: uncaught_exceptionに似ていますが、ブール結果の代わりに、キャッチされていない例外の現在の数を示すunsignedintを返します。

現在、{Clang 3.2、GCC 3.4.6、GCC 4.1.2、GCC 4.4.6、GCC 4.4.7、MSVC2005SP1、MSVC2008SP1、MSVC2010SP1、MSVC2012} x {x32、x64}でテストされています。

C ++ 11では、次の構文を使用できます

try
{
    int some_var=1;
    cout << "Case #1: stack unwinding" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
    throw 1;
} catch(int){}
{
    int some_var=1;
    cout << "Case #2: normal exit" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
}

C ++ 98では、もう少しノイズが多くなります。

try
{
    cout << "Case #1: stack unwinding" << endl;
    BOOST_SCOPE_EXIT(void) { cout << "exit" << endl; } BOOST_SCOPE_EXIT_END
    SCOPE_FAILURE(void) { cout << "failure" << endl; } SCOPE_FAILURE_END
    SCOPE_SUCCESS(void) { cout << "success" << endl; } SCOPE_SUCCESS_END
    throw 1;
} catch(int){}
{
    cout << "Case #2: normal exit" << endl;
    BOOST_SCOPE_EXIT(void) { cout << "exit" << endl; } BOOST_SCOPE_EXIT_END
    SCOPE_FAILURE(void) { cout << "failure" << endl; } SCOPE_FAILURE_END
    SCOPE_SUCCESS(void) { cout << "success" << endl; } SCOPE_SUCCESS_END
}

また、ライブラリにはUNWINDING_AWARE_DESTRUCTOR機能があります。

struct DestructorInClass
{
    UNWINDING_AWARE_DESTRUCTOR(DestructorInClass,unwinding)
    {
        cout << "DestructorInClass, unwinding: "
             << ( unwinding ? "true" : "false" ) << endl;
    }
};

ただし、UNWINDING_AWARE_DESTRUCTORが誤った結果をもたらす場合があります(ただし、スコープ(成功)およびスコープ(失敗)機能はこのような問題の影響を受けません)。

于 2013-02-08T07:44:47.603 に答える