3

コピー不可の例外クラスを作成することを考えています。コピー コンストラクター内での割り当て中にスローされる可能性のある例外について心配する必要がないため、興味深いと思います。例外オブジェクトの作成が成功した場合、すべて問題なく、std::terminate.

struct exception
{
    exception() = default;
    exception(const exception&) = delete;
    exception(exception&&) noexcept = default;
    ~exception() noexcept = default;
    auto operator=(const exception&) -> exception& = delete;
    auto operator=(exception&&) noexcept -> exception& = delete;
};

int main()
{
    try {
        try {
            throw exception{};
        } catch (...) {
            std::rethrow_exception(
                std::current_exception());
        }
    } catch (const exception& e) {
        return 1;
    }
}

GCC-4.7 と Clang-3.2 は上記のコードを受け入れます。しかし、私は少し驚いています。私の知る限り、例外オブジェクトがコピーされる状況がいくつかありstd::current_exception()ますstd::rethrow_exception()

質問:上記のコードは C++11 に従って正しいですか?つまり、C++11 に準拠するすべてのコンパイラで受け入れられますか?

編集:std::rethrow_exceptionとを例に追加std::current_exception。どちらのコンパイラもこのバージョンを受け入れます。これにより、例外がスローされたときにコンパイラがコピー コンストラクタを必要としない場合、コンパイラはこれら 2 つの関数が使用されているときにコピー コンストラクタを必要としないことが明確になります。

4

2 に答える 2

2

しかし、私は少し驚いています。私の知る限り、例外オブジェクトがコピーされる状況がいくつかありstd::current_exception()ますstd::rethrow_exception()

しかし、あなたはそれらのどれにも電話しません。標準は、例外オブジェクトがどのように初期化されるかについて非常に明確です。15.1、p3 から:

throw 式は、例外オブジェクトと呼ばれる一時オブジェクトを初期化します。その型は、throw のオペランドの静的型から最上位の cv-qualifier を削除し、「T の配列」から型を調整することによって決定されます。 「T を返す関数」を「T へのポインタ」または「T を返す関数へのポインタ」にそれぞれ変更します。一時的なものは左辺値であり、一致するハンドラー (15.3) で指定された変数を初期化するために使用されます。例外オブジェクトの型が不完全な型または (おそらく cv 修飾された) void 以外の不完全な型へのポインターである場合、プログラムは不正な形式です。これらの制限および 15.3 で説明されている型の一致に関する制限を除き、throw のオペランドは、呼び出し (5.2.2) の関数引数または return ステートメントのオペランドとして正確に扱われます。

つまり、値によって型を返すように機能します。戻り値/例外オブジェクトは、指定した式によって初期化されます。使用する式は一時的なものであるため、関数から一時的なものを返すように動作し、ムーブ コンストラクターを呼び出します。確かに、これが省略される可能性は高いですが、それが 15.1, p5 のポイントです。

スローされたオブジェクトがクラス オブジェクトの場合、コピー/移動操作が省略されていても (12.8)、コピー/移動コンストラクターとデストラクタにアクセスできる必要があります。

これは戻り値にも当てはまります。戻り値は、必要に応じてコピー/移動の初期化によって初期化されます。したがって、たとえ省略されていても、適切なコンストラクターにアクセスできる必要があります。

例外オブジェクトのコピー構築が必要な方法で例外クラスをスローすることはできません。したがって、左辺値をスローすることはできません。prvalue または xvalue のみをスローできます。

標準のどこにも、システムが理由もなく例外を任意にコピーできるとは書かれていません。を呼び出すとstd::current_exceptionコピーできます。への呼び出しstd::rethrow_exceptionは、おそらくそれをコピーします。

しかし、例外オブジェクトを明示的にコピーするものを呼び出さない場合、C++ は勝手にそうすることができません

于 2012-10-10T18:32:52.093 に答える
2

current_exceptionは、現在の例外またはそのコピーのいずれかを参照していると言いますが、どちらを参照しているかは示していません。これは私に次のことを示唆しています:

  • コピーされているかどうかは不明[*]
  • したがって、あなたの例外クラスは良くありません(誰かがそれを呼び出す可能性がある場合は確かcurrent_exceptionにそうではありません)
  • したがって、一部の実装で機能することは驚くことではありません。実装者からの要求がない限り、または実装者がコピーを避けることを望んでいない限り現在の例外がコピーされないようにするための規定はおそらくないでしょう。

物を投げて参照でキャッチするだけで問題ありません。式のテンポラリはthrow、現在の例外を保持するために実装によって使用されるオブジェクトを「初期化するために使用される」ため、(クラスがサポートするものに従って) 移動またはコピーでき、その移動/コピーは省略できます。

価値があるのは、常にコピーするようmake_exception_ptrに指定されていることです。おそらく、これは欠陥であると主張することができます: 値渡しのパラメーターである可能性がある場合、移動する方がよい場合があります。しかし、それは私の衝動的で無知な印象です。私は今までこれらの機能を見たことさえありません。e

current_exception[*] 「呼び出されるたびに新しいコピーを作成する」かどうかは明示的に指定されていませんが、最初に呼び出されたときに新しいコピーを作成するかどうかが指定されていないことを意味するかどうかは完全にはわかりません。

于 2012-10-10T16:45:28.057 に答える