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(); }
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);
To allow things like this:
std::pair<int, int> get_range(void) {
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.