Search code examples
c++boostboost-optional

How do I prevent boost::optional<T> from being constructed erroneously with 0?


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.


Solution

  • 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.