我一直在阅读该swap()
操作,例如:
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
当我们处理异常安全时是有问题的。
它有什么问题?此外,我们该如何解决呢?
我一直在阅读该swap()
操作,例如:
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
当我们处理异常安全时是有问题的。
它有什么问题?此外,我们该如何解决呢?
在一般实现中,假设T
can的任何操作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>
在内部管理它的状态。上面的一般可以抛出(分配失败,内部的构造函数可能会抛出......),但提供无抛出实现是微不足道的:begin
end
capacity
swap
T
swap
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
.
丹尼尔当然是对的。
但是你不应该忘记,当涉及到异常安全时,交换主要被认为是解决方案的一部分(“复制和交换成语”),而不是问题。剩下的问题是,如果你采用这个习惯用法,你必须确保 swap 可能调用的复制构造函数不会抛出异常,原因 Daniel 解释过。
请参阅:什么是复制和交换习语?
异常的来源已经说明
unsigned byte*
但是您可以通过将参数强制转换为并逐字节交换来避免异常,sizeof(T)
注意这不会调用对象上的任何构造函数或析构函数,这可能会在进行一些自引用时违反某些先决条件(当a
指向b
交换之后a
将指向a
)
这就是 D 标准库中的交换如何执行此操作(在检查自引用之后)