niedz., 3 mar 2019 o 22:10 Andrzej Krzemienski
sob., 2 mar 2019 o 23:32 Niall Douglas via Boost
napisał(a): You see, I would consider any union-based storage as in the same category. By definition union-based storage must contain a "pointer" to the correct way to interpret that union-based storage.
I don't understand this. Andrzej says pointers and means it literally: T*. You say "pointers" and I have no idea what you mean by it and what relation it has to exception safety.
If an object *selects* another object, then in my opinion it needs to propagate the strongest possible exception guarantees it can for assignment, swap and emplace.
If an object *aggregates* other objects, then in my opinion it is permitted to have partial operations. So swap can get half way into swapping the individual members in the aggregate, and bail out.
This might help:
T*: Selects a T[1...N] unique_ptr
: Selects a T[x] vector<T>: Selects a T[0...N] optional<T>: Selects a T[0], or a T[1] variant: Selects one of a A[1], B[1], or C[1], with all others [0] ---
struct: Aggregate of heterogeneous types T[x]: Aggregate of x homogeneous T's array
: Aggregate of x homogeneous T's Can you see the difference yet? The first group selects an aggregate *elsewhere*. The second grouo IS an aggregate.
Sorry, I cannot see how you are making this distinction. I can see a different distinction though:
T* unique_ptr
vector<T> The above types use indirection through a pointer, so providing strong assignment is cheap and quite trivial, and this is why it is provided: because it is cheap to implement: no other reason.
aggregates -- no indirection, members stored directly inside the aggregate; implementing strong assignment is expensive (double buffering), therefore we only get basic.
optional and variant also do not use indirection: they store their values in their storage, therefore providing strong assignment can be difficult.
So the driver for deciding on strong versus basic guarantee is not how we want to think about these types but a practical factor: how easy it is to implement. In case of optional<T> strong assignment is implementable "by luck": because it has the "monostate" as part of the contract. But even optional<T> has its problems: its swap() is potentially throwing if T potentially throws from move constructor or assignment, even if T's swap() is noexcept.
Correction: optional<T>::swap() 's noexcept depends on T's swap()'s and move constructor's noexcept. It does not depend on T's move assignment's noexcept.
Regards, Andrzej