4

コンストラクタ初期化子から例外をスローする最良の方法は何ですか?

例えば:

class C {
  T0 t0; // can be either valid or invalid, but does not throw directly
  T1 t1; // heavy object, do not construct if t0 is invalid,  by throwing before
  C(int n)
    : t0(n), // throw exception if t0(n) is not valid
      t1() {}
};

私は多分ラッパーを作ることを考えました、例えばt0(throw_if_invalid(n))

そのような場合を処理するための慣行は何ですか?

4

4 に答える 4

8

throw初期化する式、t0またはt1、または少なくとも1つの引数を取るコンストラクターから実行できます。

class C {
  T0 t0; // can be either valid or invalid, but does not throw directly
  T1 t1; // heavy object, do not construct if t0 is invalid, by throwing before
  C(int n) // try one of these alternatives:
    : t0( n_valid( n )? n : throw my_exc() ), // sanity pre-check
OR    t1( t0.check()? throw my_exc() : 0 ), // add dummy argument to t1::t1()
OR    t1( t0.check()? throw my_exc() : t1() ) // throw or invoke copy/move ctor
      {}
};

throw式にはvoidthrowがあり、ステートメントというよりも演算子のようになっていることに注意してください。?:演算子には、それが型の推定に干渉するのを防ぐための特別な場合がありますvoid

于 2010-04-20T04:11:35.723 に答える
2

これには複数の方法があると思います。私が理解していることからn、特定の範囲の数しかとることができません。そのためには、コンストラクターが実行されないようにすることができます。

template <typename T, T Min, T Max>
class ranged_type_c
{
public:
    typedef T value_type;

    ranged_type_c(const value_type& pX) :
    mX(pX)
    {
        check_value();
    }

    const value_type& get(void) const
    {
        return mX;
    }

    operator const value_type&(void) const
    {
        return get();
    }

    // non-const overloads would probably require a proxy
    // of some sort, to ensure values remain valid

private:
    void check_value(void)
    {
        if (mX < Min || mX > Max)
            throw std::range_error("ranged value out of range");
    }

    value_type mX;
};

もっと肉付けすることもできますが、それがアイデアです。これで、範囲をクランプできます。

struct foo_c
{
    foo_c(ranged_value_c<int, 0, 100> i) :
    x(i)
    {}

    int x;
};

0〜100以外の値を渡すと、上記がスローされます。


実行時に、私はあなたの元のアイデアが最高だったと思います:

template <typename T>
const T& check_range(const T& pX, const T& pMin, const T& pMax)
{
    if (pX < pMin || pX > pMax)
        throw std::range_error("ranged value out of range");

    return pValue;
}

struct foo
{
    foo(int i) :
    x(check_range(i, 0, 100))
    {}

    int x;
}

以上です。上記と同じですが、0と100は、有効な最小値と最大値を返す関数の呼び出しに置き換えることができます。

有効な範囲を取得するために関数呼び出しを使用することになった場合(混乱を最小限に抑え、編成をより高く保つために推奨)、オーバーロードを追加します:

template <typename T>
const T& check_range(const T& pX, const std::pair<T, T>& pRange)
{
    return check_range(pX, pRange.first, pRange.second); // unpack
}

このようなものを許可するには:

std::pair<int, int> get_range(void)
{
    // replace with some calculation
    return std::make_pair(0, 100);
}

struct foo
{
    foo(int i) :
    x(check_range(i, get_range()))
    {}

    int x;
}

選択する場合は、範囲がコンパイル時であっても、ランタイムメソッドを選択します。最適化が低くても、コンパイラーは同じコードを生成し、クラスバージョンよりもはるかに扱いにくく、間違いなく読みやすくなります。

于 2010-04-20T04:00:22.360 に答える
2

これは、初期化子リストからスローする方法です

C(int n)
    : t0(n > 0 ? n : throw std::runtime_error("barf")),
      t1() {}

「t0(n)が無効な場合は例外をスローする」と言っています。T0のコンストラクターから投げてみませんか?

オブジェクトは、構築後に有効であると想定されています。

于 2010-04-20T04:12:02.113 に答える
1

次のような場合にスローする別のクラス内にクラスT0をラップするだけです。

class ThrowingT0
{
    T0 t0;
public:
    explicit ThrowingT0(int n) : t0(n) {
        if (t0.SomeFailureMode())
            throw std::runtime_error("WTF happened.");
    };
    const T0& GetReference() const {
        return t0;
    };
    T0& GetReference() {
        return t0;
    };
};

class C
{
    ThrowingT0 t0;
    T1 t1;
public:
    explicit C(int n) : t0(n), t1() {
    };
    void SomeMemberFunctionUsingT0() {
        t0.GetReference().SomeMemberFunction();
    };
};
于 2010-04-20T04:04:13.500 に答える