在catch语句中是否可能发生复制省略?

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
{ }

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


(意思是:这段代码很丑陋,但是它是受到另一个问题的启发而产生的。)
3个回答

9
是的,在抛出和捕获异常时,都可以省略副本/移动操作。对于捕捉,只有在catch子句中指定的类型与异常对象的类型相同(除了cv限定符)时,才能省略。更详细的描述请参见C++11 12.8 / 31。
引用块: “...这种副本/移动操作的省略,称为副本省略,允许在以下情况下使用(可以组合它们以消除多个副本):”
“...”
  • 在throw表达式中,当操作数是非易失性自动对象的名称(除函数或catch子句参数之外),其范围不延伸到最内部封闭try块的末端时(如果有),则可以通过直接构造自动对象到exception对象中来省略从操作数到exception对象(15.1)的副本/移动操作
“...”
  • 当异常处理程序(第15条)的异常声明声明与异常对象(15.1)相同类型(除了cv限定符)的对象时,可以通过将异常声明视为异常对象的别名来省略副本/移动操作,前提是程序的含义不会改变,除了执行由异常声明声明的对象的构造函数和析构函数之外。

有趣,尽管引用相当狭窄...因此很难确定它实际上是指这种特定情况。 - Matthieu M.
@Mattieu:我有点懒得添加所有相关的引用,不管怎样,这就是它。 - Konstantin Oznobihin
感谢您提供的明确引用 :) - R. Martinho Fernandes

4
我认为这是被允许的。对于C++03,15.1/3中写道:
抛出表达式会初始化一个临时对象,称为异常对象。
而12/15中写道:
当一个未绑定到引用(12.2)的临时类对象将被复制到具有相同cv限定类型的类对象时,可以通过直接在省略的复制目标中构造临时对象来省略复制操作。
因此,标准定义了保存运行中异常的秘密隐藏位置为临时对象,因此适用于拷贝省略。
编辑:哎呀,我现在已经读到更深入的地方了。15.1/5中写道:
如果可以消除临时对象的使用而不改变程序的含义,除了与临时对象的使用相关联的构造函数和析构函数的执行(12.2)之外,则处理程序中的异常可以直接使用抛出表达式的参数初始化。
没有什么比这更清晰的了。
无论它是否实际上会......如果catch子句重新引发异常(包括调用可能这样做的非可见代码),则实现需要那个“称为异常对象的临时对象”仍然存在。因此,在何时可以进行拷贝省略上可能会有一些限制。显然,一个空的catch子句不能重新引发它。

0

是的。如果catch通过引用捕获异常,那么就不会有复制(这是根据定义的)。

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

C++03 §8.5/14读取:

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


这里没有引用,只有在按值捕获时才考虑复制省略。 - Matthieu M.
@Matthieu:我的回答大部分都在解决这个问题。我想你只读了第一句话? - Nawaz
关于标准引用:不清楚它是否涉及一般的复制省略或仅关于catch,您能说得更明确些吗? - Matthieu M.
我并没有只读第一句话,实际上回答的其余部分很有趣...但为什么要从OP没有问的事情开始呢? - Matthieu M.
@Matthieu:可能他想避免复制,但不知道如何操作,这就是我写第一句话的原因。 :D - Nawaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接