I've been pondering a really basic question about how far to take enforcing a class's invariant. Maybe that's worded badly, so as an example, let's say that I want to write a class which stores a limited palette of colours. The class's constructor should take the size of the palette - the idea being that you can add as many colours to the palette as you want, but it restricts the size on a FIFO policy. So we'll say that I want to arbitrarily restrict this size to the range 1 to 32 ("nobody will ever need more than 32 colours").
Let's get started...
class palette
{
public:
palette(unsigned int max_size) : m_max_size{max_size} { }
private:
unsigned int m_max_size;
};
So far, so good. Except I'm not doing anything to confirm that the max size is within my restricted range, i.e. the invariant can be broken right from construction.
(Just to clarify: this class isn't intended for use in some public library - it's simply an internal class to a single application)
Four options spring to mind beyond "do nothing":
Put a comment on the class to ask callers to use the class correctly.
// Please only call with 1 <= max_size <= 32
Assert that it's within range.
assert(max_size >= 1 && max_size <= 32);
Throw an exception if it's outside the range
if (max_size < 1 || max_size > 32) throw std::invalid_argument();
Get the compiler to check by converting to a template
template <unsigned int MAX> class palette { public: palette() { // no need for the argument any more static_assert(MAX >= 1 && MAX <= 32, "oops"); } };
Options #1 seems a little bit like wishful thinking, but maybe it's good enough for 'just' an internal class? Options #2 to #4 are increasingly fussy about the size, to the point where #4 changes the syntax so that the compiler will report an error if the class isn't being used correctly.
I'd welcome any thoughts on this. I understand that the answer will probably begin with "Well it all depends...", but I'm just fishing for some general guidelines or some alternate suggestions.