boost::optional<T>
(1.51) provides a way of constructing objects that is very dangerous for my users and that I'd like to prevent. Let's say I have my own integer class and I want to pass an optional such integer and store it in some class:
class myint {
public:
int m_a;
myint (int r_a) : m_a(r_a) {
}
};
struct myclass {
boost::optional<myint> content;
myclass (const boost::optional<myint>& arg) : content(arg) {
}
};
and now, here's how users would use the class:
myclass(myint(13)); //correct use
myclass(boost::none); //correct use
myclass(myint(0)); //correct use
myclass(0); //INCORRECT use, this easy typo
//equates boost::none which
//is not what the user meant
I'd like to understand what is going on here and prevent this behaviour.
Interestingly,
myclass(1); //does not compile
boost::none
is totally a valid value for my field, but having a boost::none
sneak-up when the user is trying to type in a 0
is horrendously misleading and dangerous.
The intent might be a bit hidden since I'm not really rolling out a myint
class and I don't really have a class myclass
that serves little to no purpose. Anyways I need to send 10 or so optional ints to a function and deduping wouldn't work. (you can imagine I asked you for your age, your height and your wealth and that there's three special buttons to check if you don't want to answer a question)
I've posted an answer that seems to work below (built from Mooing's Duck & Ilonesmiz suggestion, but lighter). I'm happy to hear comments about it, though.
This code (Inspired from Ilonesmiz) seems to do the job fine and is a bit lighter than the approach from Mooing Duck but still uses the magic templating trick.
struct myprotectedclass {
boost::optional<myint> content;
template <class T>
myprotectedclass(const T& a) :content(boost::optional<myint>(a)) {}
};
Here is the proof.
When C++ sees the 0
, it thinks "hmm, this is probably an int, but it might be a pointer to something!" (only for 0
, no other numbers) But if you pass that 0
to a function, it must decide on a type, and so it picks the default of int
. Whereas, in the original, a 0
was passed to a function expecting a myint
or a pointer (boost::none_t
is a pointer). 0
isn't a myint, but it can be a pointer, so it was picking that one.