有关swap()操作的异常安全性 - 这有什么问题?

时间:2012-07-14 17:15:32

标签: c++ exception exception-handling exception-safety

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

template<class T>
void swap (T &a, T &b)
{
  T temp (a);
  a = b;
  b = temp;
}
当我们处理Exception-safety时,

是有问题的。

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

3 个答案:

答案 0 :(得分:22)

在一般实现中,假设T的任何操作都可以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已经被修改,这意味着在异常传播到堆栈之后,调用者将留下既不是原始状态也不是最终状态的状态。 / p>


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

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

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提供了无抛出保证,如果你总是按照上面的模式实现swapusing std::swap;后跟{swap的无限制调用{ {1}})它将被ADL选为比std::swap更好的匹配。

答案 1 :(得分:2)

丹尼尔当然是对的。

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

请参阅:What is the copy-and-swap idiom?

答案 2 :(得分:0)

已解释了异常的来源

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

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