14

少し前に、型のフィールドを持つ例外クラスを作成すべきではないと聞いたことがありますstd::string。それがブーストのウェブサイトが言っていることです。その根拠は、std::stringメモリ割り当てが失敗した場合にコピー コンストラクターが例外をスローする可能性があり、現在処理されている例外がキャッチされる前に例外がスローされると、プログラムが終了することです。

しかし、それはムーブ コンストラクターの世界でも通用するのでしょうか? 例外をスローするときに、コピー コンストラクターの代わりにムーブ コンストラクターを使用しませんか? C++11 ではメモリ割り当てが行われず、例外が発生する可能性がなく、std::string現在例外クラスで完全に問題がないことを正しく理解していますか?

4

2 に答える 2

14

答えは次のとおりです。

std::stringはい、例外タイプに a を埋め込みたくありません。例外は、知らないうちにコピーされることがよくあります。たとえば、一部のプラットフォームでstd::rethrow_exceptionは例外がコピーされます (一部のプラットフォームではコピーされません)。

ベスト プラクティスとして、コピー コンストラクターを保持してくださいnoexcept

しかし、すべてが失われるわけではありません。あまり知られていない事実として、C++ は常に不変の ref-counted 文字列型 (例外をスローしないコピー コンストラクターを使用) を標準内に持ち、難読化された名前だけを使用していました。実際には2つの名前:

logic_error
runtime_error

これらの型の仕様では、不変の参照カウントされた文字列のようなオブジェクトを含める必要があります。まあ、完全に不変というわけではありません。文字列を割り当てに置き換えることができます。ただし、それ以外の場合、文字列をその場で変更することはできません。

私のアドバイスは、これらの型のいずれかから派生させるか、それが受け入れられない場合は、これらの型のいずれかを埋め込み、それを不変の参照カウント文字列型として扱うことです。

#include <stdexcept>
#include <iostream>

class error1
    : public std::runtime_error
{
    using msg_ = std::runtime_error;
public:
    explicit error1(std::string const& msg)
        : msg_(msg)
    {}
};

class error2
{
    std::runtime_error msg_;
public:
    explicit error2(std::string const& msg)
        : msg_(msg)
    {}

    char const* what() const noexcept {return msg_.what();}
};

void
test_error1()
{
    try
    {
        throw error1("test1");
    }
    catch (error1 const& e)
    {
        std::cout << e.what() << '\n';
    }
}

void
test_error2()
{
    try
    {
        throw error2("test2");
    }
    catch (error2 const& e)
    {
        std::cout << e.what() << '\n';
    }
}

int
main()
{
    test_error1();
    test_error2();
}

std::lib はすべての文字列処理とメモリ管理を処理noexceptし、お買い得にコピーできます。

static_assert(std::is_nothrow_copy_constructible<error1>{}, "");
static_assert(std::is_nothrow_copy_assignable   <error1>{}, "");
static_assert(std::is_nothrow_copy_constructible<error2>{}, "");
static_assert(std::is_nothrow_copy_assignable   <error2>{}, "");
于 2014-07-01T23:34:03.647 に答える