2017-05-27 20:56 GMT+02:00 Peter Dimov via Boost
Andrzej Krzemienski wrote:
Destroy-only is a completely different animal.
What? How is that different? Surely, when a basic-guarantee operation failed on object `o`, you cannot safely use all operations on `o` that you were allowed to use before the throw.
Yes, you can. That's the whole point. It's as if someone handed you a valid object `o`. You don't know what's inside, but you know that it's not destroy-only. It has some value, you just don't know what it is.
Ok, but even without any exception you cannot call some functions on your objects, because individual member functions still have preconditions. Consider std::optional: optional<int> o = 1; use_int(*o); // fine o.emplace(this_returns_int_but_throws()); // basic exception safety use_int(*o); // UB, even though o is in "valid state"
Sure, you can abide by the letter of the basic guarantee while trampling all over its spirit by littering all member functions with a "!valueless()" precondition, but this doesn't change much.
Ok, I understand.
In other words, under basic guarantee, r.has_value() returns true or false, under destroy-only it crashes.
In yet other words, under basic guarantee, you in general have wide contract functions, under destroy-only, you never do (except for the destructor and possibly assignment.)
I have checked "Exception-Safety in Generic Components" at http://www.boost.org/community/exception_safety.html It says, "The basic guarantee: that the *invariants of the component are preserved*, and no resources are leaked." I suppose either interpretation is correct, depending on how strong you wan the invariant to be. But tell me this. Consider the example with class Man above: ``` struct Man { std::string fist_name, last_name; }; Man m1 = {"April", "Jones"}; Man m2 = {"Theresa", "May"}; try { m2 = m1; // suppose it throws } catch(...) { } ``` Object m2 after recovering from stack unwinding may be in the state {"April", "May"}, which is a "valid state". Would you call it a valid state? It is "valid" in the sense that reading values from its members does not cause UB, but its "high-level invariant" (that m1 should refer to an existing person) is broken. It conveys no useful information. Once you observe it sneaked out of "stack unwinding bubble", the only reasonable thing you can do with it is to either reset it to a new meaningful value, of just continue with stack unwinding until m1 gets out of scope. Maybe you can see other usages, but I don't. And because I am only interested in reset or destroy, the fact that I can read values from its members is of no use to me. Same with outcome<T>: if I assign to it and it fails: o1 = o2; // assume basic guarantee provided I get transactional guarantee, I know what its value is. But if I get "basic guarantee" as you describe it (valid but unspecfied state), what good does it make that I can safely call has_value() if the object contains a different value than o1 or o2 had initially? How can I trust such value even if I can read it? I can only discard it. Regards, &rzej;