10

我一直在阅读该swap()操作,例如:

template<class T>
void swap (T &a, T &b)
{
  T temp (a);
  a = b;
  b = temp;
}

当我们处理异常安全时是有问题的。

它有什么问题?此外,我们该如何解决呢?

4

3 回答 3

22

在一般实现中,假设Tcan的任何操作throw,您都无法提供强异常保证,这意味着在异常事件发生时将状态完全保留在操作之前的状态。即使每个操作都T提供了强大的异常保证:

template<class T>
void swap (T &a, T &b)
{
  T temp (a);          // [1]
  a = b;               // [2]
  b = temp;            // [3]
}

如果 [1] 抛出,输入保持不变,这很好。如果 [2] 抛出,并假设强异常保证,这些值仍然保持不变,这很好。但是如果是 [3] 抛出的,a已经被修改了,这意味着异常向上传播后,调用者将处于一个既不是原始状态也不是最终状态的状态。


编辑:此外,我们如何解决它?

没有通用的解决方案。但在大多数情况下,您可以swap为您的类型提供异常安全操作。考虑 a ,它通过使用三个指针 ( , , )vector<T>在内部管理它的状态。上面的一般可以抛出(分配失败,内部的构造函数可能会抛出......),但提供无抛出实现是微不足道的:beginendcapacityswapTswap

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);
}

因为指针的复制不能抛出,所以swap上面提供了不抛出保证,如果你总是swap按照上面的模式实现(using std::swap;后面跟着不合格的调用swap),ADL 将把它作为比std::swap.

于 2012-07-14T17:23:55.993 回答
2

丹尼尔当然是对的。

但是你不应该忘记,当涉及到异常安全时,交换主要被认为是解决方案的一部分(“复制和交换成语”),而不是问题。剩下的问题是,如果你采用这个习惯用法,你必须确保 swap 可能调用的复制构造函数不会抛出异常,原因 Daniel 解释过。

请参阅:什么是复制和交换习语?

于 2012-07-14T17:37:01.943 回答
0

异常的来源已经说明

unsigned byte*但是您可以通过将参数强制转换为并逐字节交换来避免异常,sizeof(T)注意这不会调用对象上的任何构造函数或析构函数,这可能会在进行一些自引用时违反某些先决条件(当a指向b交换之后a将指向a)

这就是 D 标准库中的交换如何执行此操作(在检查自引用之后)

于 2012-07-14T21:54:11.300 回答