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.
Do not let the constructor take a boost::optional
I would do something like this instead.
struct myclass {
boost::optional<myint> content;
myclass () = default;
explicit myclass(const myint& int_):content(int_){}
};
However when I am thinking about it I am not completely clear on what you are trying to achieve and what you want to avoid happening. What is the purpose of the optional
member?
This is uglier than I like, but it seems to address your concerns. It works by forwarding the argument given to myclass
perfectly to a pair of functions that take either an int
or a boost::none_t
, bypassing the implicit user-defined constructor. This works because 0
matches int
better than boost::none_t
, and an implicit user-defined constructor is the worst match.
class myint {
public:
int m_a;
myint (int r_a) : m_a(r_a) {}
};
boost::optional<myint> myintctor(int arg) {return myint(arg);}
boost::optional<myint> myintctor(boost::none_t arg) {return arg;}
struct myclass {
boost::optional<myint> content0;
boost::optional<myint> content1;
boost::optional<myint> content2;
template<class T0, class T1, class T2>
myclass(const T0& a0, const T1& a1, const T2& a2)
:content0(myintctor(a0)), content1(myintctor(a1)), content2(myintctor(a2))
{}
};
Proof of concept. Modern compilers ought to be smart enough to elide the copy, but that shouldn't matter for an int
.
I guess the problem is only meaningful for optional int
. One solution could be to provide two constructors:
myclass() : content(boost::none) {}
myclass(myint input) : content(input) {}
It's true that you lose a bit the advantage of boost::optional
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With