śr., 3 kwi 2019 o 01:33 Gavin Lambert via Boost
On 3/04/2019 05:27, Andrzej Krzemienski wrote:
This is not a full review yet. I just wanted to discuss the design first, especially the never-empty guarantee. And I am going to rehash the old argument.
The core of variant2 is its never-empty guarantee and the mechanism to achieve it: a number of different algorithms chosen depending on what the potentially stored types permit. In fact this mechanism is not much different than the one present in boost::variant.
I agree with most of the points that Andrzej has made, although I would go a step further and question why people want a never-empty variant in the first place.
There is always potential uncertainty about the actual content type of a variant, requiring either assumptions (and UB or exceptions if wrong) or explicitly testing, eg. with visit. Checking if a variant is empty seems no different to me than checking if it contains an int; eliding either check confers exactly the same risks.
Requiring that variants always possess an empty state dramatically simplifies implementation, including default construction and move assignment. And it avoids the valueless_by_exception kludge by promoting it to an entirely expected possible state of variant. (Thereby increasing the chance that consumers will consider it.)
It also means that (like traditional monads), variant<T> is behaviorally equivalent (bar some naming conventions) to optional<T>, which seems like a consistent conceptual choice. And variant<> is valid and can only hold the empty state.
In principle, the stronger the invariant, the fewer opportunities to make bug. The strive for never-empty variant is conceptually similar to a strive for a pointer that does not have the null-pointer state. We often give advice to people that as long as this is possible you should prefer using references to using pointers, because the former do not have the null state. The significant portion of crashes in the programs I have worked with was when a null pointer was dereferenced. Function f() returns a shared_ptr<> and the implied contract is, it is not null. I do not want to check for null pointer whenever I use it. But at some point some clever developer comes to an idea that if he doesn't know what to do, he will return a null pointer. These problems would not happen if there was a type never_null_shared_ptr. It just wipes a class of bugs from existence. I am myself in favor of never-empty guarantee for variant, as long as it does not cost too much. The notion of how easy/hard it is to implement something should not drive the interface and the contract of a type. I object to the never-empty guarantee for variant because it costs too much and affects the contract of the type. In C++ performance characteristics of a type are part of its contract. Regards, &rzej;
Furthermore, while I can see possible value in a never-empty strong-guarantee variant (although I remain unconvinced that the "never-empty" part provides much benefit, as above); as soon as this guarantee is weakened then I don't see any advantage at all in trying to maintain a "never-empty" property. Unexpectedly changing type and/or content is exactly as bad as unexpectedly becoming empty.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost