问题
I have been looking for a facility to exchange two items with a strong exception guarantee. That is; to have the exchange proceed totally, or leave the target in the initial state in the face of exceptions. Is there anything included in the current standard that allows this, I have not been able to find anything, although it appears easy to write.
What I have below is a version I put together to try out what I am looking for, however it is noexcept, which is more than my requirement of "the strong" guarantee. Is appears the "strong guarantee" cannot be tested, but the noexcept guarantee can.
#include <iostream>
#include <type_traits>
// Guarantee to exchange l and r fully
// or (not compile) leave them in the initial state
template<typename T>
void strong_exchange(T & l, T &r) noexcept
{
using std::swap;
static_assert( noexcept( swap(l, r) ), "Types must be noexcept swappable");
swap(l, r);
}
struct X
{
X()
{
throw "fish";
}
X(X &&) = delete;
};
int main(void)
{
int a, b;
strong_exchange(a, b);
X i, j;
strong_exchange(i, j);
}
回答1:
Probably impossible:
It is impossible if copy assignment is not noexcept
(or other way to perform the copy). In case it is noexcept
, std::swap()
should do the thing. Otherwise, there is probably nothing one can do about it.
回答2:
@Incomputable has highlighted the problem, but I am including an answer for completeness.
- There is a class you want to assign but want to make sure there is a strong guarantee that it will be copied completely or not at all in the face of exceptions.. The only feasible solution therefore appears to be a static assert check at compile time to make sure that this will happen. Using std::move is not a guarantee since it can call a copy constructor which may throw, and may also fail to provide the all or nothing guarantee.
- In my specific case it is a set of configuration objects which must always be valid, and since I can’t guarantee that without checking all the objects manually and hoping the implementors can provide that guarantee, I have written a noecept_assign function which should ensure that is the case going forward. A complete example follows.
#include <iostream> #include <type_traits> // Guarantee to exchange l and r fully or not compile. template<typename T> void noexcept_swap(T & l, T &r) noexcept { using std::swap; static_assert( noexcept( swap(l, r) ), "Types must be noexcept swappable"); swap(l, r); } // Guarantee to full assign r to l or not compile. template<typename T> void noexcept_assign(T & l, T r) noexcept { noexcept_swap(l, r); } struct Exchangeable { Exchangeable() { } // C++ std::swap requires these two for now thorw, so noexcept_swap will also need them Exchangeable(Exchangeable &&) noexcept { } Exchangeable & operator=(Exchangeable &&) noexcept { } // for noexcept_assign these is also required, since a copy is made durring assignment Exchangeable(Exchangeable &) noexcept { } Exchangeable & operator=(const Exchangeable &) noexcept { } }; // This class is the same as the above, but it does not have // a noexcept guarantee on the methods struct NotExchangeable { NotExchangeable() {} NotExchangeable(NotExchangeable &&) {} NotExchangeable & operator=(NotExchangeable &&) { } NotExchangeable(const NotExchangeable &) { } NotExchangeable & operator=(const NotExchangeable &) { } }; int main(void) { int a, b; noexcept_swap(a, b); // OK noexcept_assign(a, b); // OK Exchangeable i, j; i = j; // Might throw and fail to do a full copy. Depends. noexcept_swap(i, j); // OK noexcept_assign(i, j); // OK NotExchangeable x, y; #if 0 noexcept_swap(x, y); // Fails to compile and emits assertion noexcept_assign(x, y); // Fails to compile and emits assertion #endif }
来源:https://stackoverflow.com/questions/42088173/is-there-facility-for-a-strong-guaranteed-exchange-in-c