In the general implementation, assuming that any operation of T
can throw
, you cannot provide the strong exception guarantee which implies leaving the state exactly as it was before the operation on the event of a exception. Even if each operation on T
offers the strong exception guarantee:
template<class T>
void swap (T &a, T &b)
{
T temp (a); // [1]
a = b; // [2]
b = temp; // [3]
}
If [1] throws, the input is left untouched, which is good. If [2] throws, and assuming the strong exception guarantee, the values are still untouched, which is good. But if it is [3] that throws, a
has already been modified, which means that after the exception propagates up the stack, the caller will be left with a state that is neither the original nor the final states.
EDIT: Furthermore, how can we solve it?
There is no general solution. But in most cases you can provide a exception safe swap
operation for your types. Consider a vector<T>
, which internally manages it's state through the use of three pointers (begin
, end
, capacity
). The general swap
above can throw (fail to allocate, the constructors for the internal T
might throw...), but it is trivial to provide a no-throw swap
implementation:
template <typename T>
class vector {
T *b,*e,*c;
public:
void swap( vector<T>& rhs ) {
using std::swap;
swap( b, rhs.b );
swap( e, rhs.e );
swap( c, rhs.c );
}
//...
};
template <typename T>
void swap( vector<T>& lhs, vector<T>& rhs ) {
lhs.swap(rhs);
}
Because copying of pointers cannot throw, the swap
above offers the no-throw guarantee, and if you always implement swap
following the pattern above (using std::swap;
followed by unqualified calls to swap
) it will be picked up by ADL as a better match than std::swap
.