Scott Meyers说:
C ++指定始终复制作为异常抛出的对象,并由对象的复制构造函数执行复制。
但在我的代码中:
struct test
{
test() { cout << "constructor is called" << endl; }
test(const test&) { cout << "copy constructor is called" << endl; }
~test() { cout << "destructor is called" << endl; }
};
void fun()
{
throw test();
}
int main()
{
try {
fun();
}
catch (test& t1) { cout << "exception handler" << endl; }
}
我没有看到调用异常对象的复制构造函数。
如果我更改catch
以按值接收异常对象,那么它是,但根据Meyers的引用,异常对象应该被复制,即使它是通过引用接收的。
为什么不调用复制构造函数(即使通过引用执行异常处理)?
答案 0 :(得分:11)
Meyers是正确的,在语义上制作副本:
[C++11: 12.2/1]:
类型的临时代码在各种上下文中创建:绑定对prvalue的引用(8.5.3),返回prvalue(6.6.3),创建的转换prvalue(4.1,5.2.9,5.2.11,5.4),抛出异常(15.1),进入处理程序(15.3),以及某些初始化(8.5)。 [..]
[C++11: 15.1/4]:
抛出异常的临时副本的内存以未指定的方式分配,除非在3.7.3.1中说明。只要存在为该异常执行的处理程序,临时就会持续存在。
但是,聪明的编译器可以省略副本,不管副作用如何,都可以这样做。
[C++11: 12.8/31]:
当满足某些条件时,允许实现省略类对象的复制/移动构造,即使复制/移动构造函数和/或析构函数为对象有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间。没有优化就被破坏了。复制/移动操作的省略,称为复制省略,在以下情况下允许(可以合并以消除多个副本):
- [..]
- 当尚未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualified类型的类对象时, 通过将临时对象直接构造到省略的复制/移动的目标中,可以省略复制/移动操作。
- [..]