我一直在阅读swap()
操作,例如:
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
当我们处理Exception-safety时,是有问题的。
它有什么问题? 此外,我们如何解决它?
答案 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>
,它通过使用三个指针(begin
,end
,capacity
)在内部管理它的状态。上面的通用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
提供了无抛出保证,如果你总是按照上面的模式实现swap
(using std::swap;
后跟{swap
的无限制调用{ {1}})它将被ADL选为比std::swap
更好的匹配。
答案 1 :(得分:2)
丹尼尔当然是对的。
但是你不应该忘记,当谈到异常安全时,交换主要被认为是解决方案的一部分(“复制和交换习语”),而不是问题。剩下的问题是,如果你采用这个习惯用法,你必须确保交换的拷贝构造函数可以调用,不要抛出异常,因为Daniel解释过。
答案 2 :(得分:0)
已解释了异常的来源
但是你可以通过将参数转换为unsigned byte*
并逐字节交换到sizeof(T)
来避免异常。注意这不会调用对象上的任何构造函数或析构函数,这可能会违反某些先决条件正在进行一些自我引用(在a
指向b
之后a
指向a
时
这就是D标准库中的交换如何(在检查自引用之后)