I think, it is easier explain using an example. Let’s take a class that model the speed of a Formula 1 car, the interface may look something like:
class SpeedF1
{
public:
explicit SpeedF1(double speed);
double getSpeed() const;
void setSpeed(double newSpeed);
//other stuff as unit
private:
double speed_;
};
Now, negative speed are not meaningful in this particular case and neither value greater then 500 km/h. In this case the constructor and the setSpeed function may throw exceptions if the value provide is not within a logical range.
I can introduce an extra layer of abstraction and insert a extra object instead of double.
The new object will be a wrapper around the double and it will be construct and never modify.
The interface of the class will be changed to:
class ReasonableSpeed
{
public:
explicit ReasonableSpeed(double speed); //may throw a logic error
double getSpeed() const;
//no setter are provide
private:
double speed_;
};
class SpeedF1
{
public:
explicit SpeedF1(const ReasonableSpeed& speed);
ReasonableSpeed getSpeed() const;
void setSpeed(const ReasonableSpeed& newSpeed);
//other stuff as unit
private:
ReasonableSpeed speed_;
};
With this design SpeedF1 cannot throw, however I need to extra pay an object constructor every time I want to reset the speed.
For class where a limited set of value are reasonable (for example the Months in a calendar) I usually make the constructor private and provide a complete set of static functions. In this case it is impossible, another possibility is implement a null object pattern but I am not sure it is superior to simply throw an exception.
Finally, my question is:
What is the best practise to solve this kind of problem?
First off, don’t overestimate the cost of the extra constructor. In fact, this cost should be exactly the cost of initialising a
doubleplus the cost for the validity check. In other words, it is likely equal to using a rawdouble.Secondly, lose the setter. Setters – and, to a lesser degree, getters – are almost always anti-patterns. If you need to set a new (maximum) speed, chances are you actually want a new car.
Now, about the actual problem: a throwing constructor is completely fine in principle. Don’t write convoluted code to avoid such a construct.
On the other hand, I also like the idea of self-checking types. This makes the best use of C++’ type system and I’m all in favour of that.
Both alternatives have their advantages. Which one is best really depends on the exact situation. In general, I try to exploit the type system and static type checking as much as possible. In your case, this would mean having an extra type for the speed.