Throw an exception from the constructor initializer

What is the best way to throw an exception from the constructor initializer?

For instance:

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() {} }; 

I thought maybe make a wrapper, for example. t0(throw_if_invalid(n)) .

What is the practice of dealing with such cases?

+4
source share
4 answers

There are several ways around this, I think. As I understand it, n can only accept a specific range of numbers. To do this, you can even prevent the constructor from starting:

 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; }; 

May be more outlined, but this idea. Now you can hold the range:

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

If you pass a value that does not lie between 0-100, then a value will be selected above.


At runtime, I think your original idea was the best:

 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; } 

What is it. The same as above, but 0 and 100 can be replaced by calling some function that returns a real minimum and maximum.

If you end up using a function call to get valid ranges (recommended to keep the interference to a minimum and the organization above), I would add overload:

 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 } 

To allow things like this:

 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; } 

If I had to choose, I would choose the execution methods, even if the range was compilation time. Even with low optimization, the compiler will generate the same code, and it is much less awkward and possibly cleaner than the version of the class.

+2
source

You can throw from an expression (s) that initializes t0 or t1 , or any constructor that takes at least one argument.

 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 {} }; 

Note that the throw expression is of type void , which makes throw more like an operator than an operator. The ?: Operator has a special case to prevent void interfering with its type deduction.

+8
source

This is a way to drop from the list of initializers

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

You say "throw exception if t0 (n) is invalid". Why don't you get out of the T0 constructor?

The object must be valid after construction.

+2
source

Just wrap the class T0 inside another class that does this in the following cases:

 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(); }; }; 
+1
source

Source: https://habr.com/ru/post/1307413/


All Articles