Usually when a function returns boost::optional I've seen a lot of people returning an empty brace {} to designate an empty value, that works fine and is shorter than returning boost::none.
I tried to do something similar to empty a boost::optional<int>, but when calling the copy assignment operator (or most probably the move assignment op) with an empty brace in the right side, the empty brace is converted to an int and then that value is assigned to the optional, so I end up with the variable set to 0 and not an empty value as I was expecting. Here's an example https://godbolt.org/g/HiF92v, if I try the same with std::experimental::optional I get the result I'm expecting (just replace with std::experimental::optional in the example and you will see that the instruction becomes mov eax, eax).
Also if I try with a different template argument for the boost optional (a non integer type) some compilers compile (with the behavior I'm expecting, an example here http://cpp.sh/5j7n) and others don't. So even for the same lib the behavior is different according to the template arg.
I'd like to understand what is going on here, I know it has something to do with the fact that I'm using a C++14 feature for a library that doesn't consider that into the design. I read the boost/optional header but I got lost in the details, I also tried to study the compiled code without inlining with a similar result.
I'm using gcc 4.9.2 with -std=c++14 and boost 1.57.
btw: I known I should have used boost::optional::reset or boost::none, but I was trying to be consistent with the semantics in the rest of the code base.
To understand what is going on, consider this example first:
This results in a compiler error, because the overload resolution is inconclusive:
doubleandintfit equally well. But, if a non-scalar type comes into play, the situation is different:This is because a scalar is a better match. If, for some reason, I want the same two overloads but at the same time I would like
fun({})to select overloadfun(Wrap), I can tweak the definitions a bit:That is,
fun(Wrap)remains unchanged, but the first overload is now a template that takes anyT. But withenable_ifwe constrain it, so that it only works with typeint. So, this is quite an 'artificial' template, but it does the job. If I call:The artificial template gets selected. But if I type:
The artificial template is still a template, so it is never considered in type deduction in this case, and the only visible overload is
fun(Wrap), so it gets selected.The same trick is employed in
std::optional<T>: it does not have an assignment fromT. Instead it has a similar artificial assignment template that takes anyU, but is later constrained, so thatT == U. You can see it in the reference implementation here.boost::optional<T>has been implemented before C++11, unaware of this 'reset idiom'. Therefore it has a normal assignment fromT, and in cases whereThappens to be a scalar this assignment fromTis preferred. Hence the difference.Given all that, I think that Boost.Optional has a bug that it does something opposite than
std::optional. Even if it is not implementable in Boost.Optional, it should at least fail to compile, in order to avoid run-time surprises.