为什么将 swap() 实现为非抛出

Why implementing swap() as non-throwing

我理解为什么有时建议为给定的 class 实现我们自己的 swap() 函数。

例如,如果我们在 pimpl 习语之后有一个 class,我们可能想要定义我们自己的 复制构造函数,以便它对作为参数传递的对象的内容执行深度复制,而不是执行 浅复制 默认复制构造函数。这同样适用于 复制赋值运算符 .

因为 std::swap() 似乎是根据(至少,当涉及到 C++03 时) 复制构造函数 复制赋值运算符。执行要交换的对象的深拷贝是低效的,因为只需要交换这些对象包含的指针。

我的问题是为什么我们应该将swap()函数实现为非抛出一个.

在上面解释的情况下,我认为这只是语义问题:因为没有分配新资源(即:两个现有指针只是被交换)。这样的函数抛出异常意义不大。

但是,我在这个推理中可能忽略了其他原因或场景。

编辑

我最初将这个问题理解为“为什么要在 swap 函数上使用 throw 说明符。这个答案可能与主题无关,因为我没有解释为什么 swap 从不抛出。


我认为最好的答案是为什么不呢?

当这个函数没有理由抛出时,为什么不指定该函数永远不会抛出?

当函数没有理由抛出异常时,你应该始终将函数实现为非抛出:你为你的函数提供了更强的保证。

此外,通过一些元编程,您可以利用函数的非抛出特性。当 swap/copy/move(C++11) 不抛出时,一些 STL 类 使用它来获得更快的成员函数。 (实际上我不确定他们是否利用了在 C++11 之前的代码中不会抛出的函数)

对于一些类例如

a class following the pimpl idiom

我们知道swap的执行不需要抛出因为

It wouldn't make much sense for such a kind of function to throw an exception.

当抛出异常没有意义的时候,那么最好不要抛出异常。


可能还有其他 类,例如那些包含没有专门交换函数的复杂成员,但可能会抛出复制构造函数/赋值。对于这样的 类 我们无法实现永不抛出的交换。

swap在你的 pImples can't fail 中,在一个格式良好的程序中。 (并且格式错误的程序中的行为无关紧要)。没什么可扔的

My question is why we should implement our swap() function as a non-throwing one

  1. 因为swap万一抛出就完全没用了

    考虑:您 swap 两个实例,操作抛出。现在,他们处于什么状态?

    强有力的保证是在抛出异常时没有副作用,这意味着两个原始对象都保留在其原始状态。

    如果我们不能满足强保证,在很多情况下我们根本无法使用swap,因为没有办法从失败中有效地恢复,并且根本没有必要编写 swap 的那个版本。

  2. 因为没有理由抛出。

    swap(现在)的简单实现使用移动赋值和构造。

    移动构造函数通常没有理由抛出:它不分配任何新内容,它只是重新安置现有数据。移动赋值通常没有理由抛出(如上所述),并且析构函数永远不应该抛出 - 这些是唯一需要的操作。