8

考虑一个带有副作用的复制构造函数的异常类。

编译器可以在这里跳过调用复制构造函数吗:

try {
    throw ugly_exception();
}
catch(ugly_exception) // ignoring the exception, so I'm not naming it
{ }

那这个呢:

try {
    something_that_throws_ugly_exception();
}
catch(ugly_exception) // ignoring the exception, so I'm not naming it
{ }

(是的,我知道这一切都很丑陋,这是受另一个问题的启发)

4

3 回答 3

9

是的,在投掷和接球过程中都可以忽略它。只有当 catch 子句中指定的类型与异常对象的类型相同(除了 cv-qualifications)时,才能忽略它。有关更正式和详细的描述,请参阅 C++11 12.8/31。

...这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):

...

  • 在 throw 表达式中,当操作数是非易失性自动对象(函数或 catch 子句参数除外)的名称时,其范围不超出最内层封闭 try 块的末尾(如果有),从操作数到异常对象(15.1)的复制/移动操作可以通过将自动对象直接构造到异常对象中来省略

...

  • 当异常处理程序的异常声明(第 15 条)声明与异常对象(15.1)具有相同类型的对象(cv 限定除外)时,可以通过将异常声明视为省略复制/移动操作异常对象的别名,如果程序的含义将保持不变,除了为异常声明声明的对象执行构造函数和析构函数。
于 2011-09-13T12:21:08.567 回答
4

我认为这是特别允许的。对于 C++03,15.1/3 说:

throw-expression 初始化一个临时对象,称为异常对象,

和 12/15 说:

当尚未绑定到引用(12.2)的临时类对象将被复制到具有相同 cv 非限定类型的类对象时,可以通过将临时对象直接构造到省略副本

因此,保存运行中异常的秘密隐藏位置被标准定义为临时的,因此对复制省略有效。

编辑:哎呀,我现在已经进一步阅读。15.1/5:

如果除了执行与使用临时对象相关的构造函数和析构函数(12.2)之外,可以在不改变程序含义的情况下消除临时对象的使用,则可以直接使用参数初始化处理程序中的异常的 throw 表达式。

没有变得更清楚。

它是否真的会......如果 catch 子句要重新引发异常(包括如果它调用可能这样做的不可见代码),那么实现需要“称为异常对象的临时对象”仍然存在. 因此,何时可以进行复制省略可能会有一些限制。显然,一个空的 catch 子句不能重新提出它。

于 2011-09-13T12:22:44.800 回答
0

是的。如果通过引用catch捕获异常,则不会有副本(嗯,这是根据定义)。

但我认为这不是你的问题,我相信你写的代码是故意写的,没有提到reference。如果是这种情况,那么是的,即使在这种情况下,也可以省略复制。实际上,变量的初始化理论上catch直接初始化。编译器可以在可能的情况下省略直接初始化中的复制。

C++03 §8.5/14 读取,

[...] 在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除这种直接初始化中固有的复制;

于 2011-09-13T12:11:14.833 回答