I moved this discussion from the other thread.
optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // this compiles and does the wrong thing }
1) When we are bringing T into the optional<T> land, we apply implicit
constructor. For example, we provide T to a function taking optional<T>. Here, the direction of the conversion is unambiguous. Namely, T to optional<T>. So, we apply it.
2) When the direction of conversion is not as clear, we refuse applying it
and leave it to the user. op<(T, optional<T>) would be one such example.
Oh, c'mon. Cheer up. Things are not as gloomy, are they. :-) Here the subtlety (IMO) lies in how reasonable it is to add information. Namely, as I indicated, if we bring T into the optional<T> fold, then we apply optional<T> rules to T, i.e. apply t to optional<T> conversion. Say, we have a mandarin and an orange. When we bring the mandarin to an orange factory, then mandarin-to-orange "conversion" is applied and for all purposes mandarin is a "small orange". Outside the factory, when we compare mandarin to orange, it is not immediately clear what should be treated as what. If we apply mandarin-to-orange "conversion", then it'll be "small orange vs. big orange". If instead we apply orange-to-mandarin "conversion", then it'll be "sweet mandarin vs. sour mandarin". Given the library writer does not know, which it is, we ban it. Still not convinced? I've spent all munition I had. :-)
When you are talking about oranges and mandarins outside the context of C++, I am convinced. the problem I have is with mapping it onto C++ and the design of Optional. (1) In C++ we have the converting constructor. This appears close to what you call "bringing T into the optional<T> land", but I claim it is not the same. And the consequences of these nuance differences result in the problem in question. Perhaps the notion of "bringing T into the optional<T> land" would be better reflected by an explicit constructor, or function make_optional(). I am not saying that the converting constructor is wrong here. I am just saying that the motivation is *slightly* different than "bringing T into the optional<T> land", it is more for syntactic convenience, which is almost the same, but not same. (2) Your philosophy "when the direction of conversion is not as clear, we refuse applying it" -- there is no way to apply it within the definition of optional. We can apply it by poisoning every operation in the world that takes T or optional<T>. But that looks impractical. Back to Vicente's concern: User defining its own function
void f(optional<T>, optional<T>);
would need to add the following?
void f(T, optional<T>) { BOOST_STATIC_ASSERT(); } void f(optional<T>, T) { BOOST_STATIC_ASSERT(); }
What if there are 3 optional parameters? We can not say to the user that they need to program this way.
I do not think it has been addressed.