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从初始化t0or的表达式t1或任何至少接受一个参数的构造函数中获取。

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表达式具有void类型,因此throw更像是运算符而不是语句。?:运算符有一个特殊情况,以防止其干扰void其类型推导。

于 2010-04-20T04:11:35.723 回答
2

这是一种从初始化列表中抛出的方法

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

您是说“如果 t0(n) 无效则抛出异常”。为什么不从 T0 的构造函数中抛出?

一个对象应该在构造后是有效的。

于 2010-04-20T04:12:02.113 回答
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 回答
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 回答