如果抛出的异常始终是异常对象的副本,为什么不调用此复制构造函数?

Alo*_*lok 9 c++

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; }
}
Run Code Online (Sandbox Code Playgroud)

我没有看到异常对象的复制构造函数被调用.

如果我更改catch按值接收异常对象,那么它是,但根据Meyers的引用,异常对象应该被复制,即使它是通过引用接收的.

为什么不调用复制构造函数(即使通过引用执行异常处理)?

Lig*_*ica 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-nonqualified类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动.
  • [..]

  • 对于12.8/31,我们实际上是在下一种情况下,"当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unquali fi ed类型的类对象时,复制/通过将临时对象直接构造到省略的复制/移动的目标中,可以省略移动操作.`test()`是临时的,以未指定的方式分配的异常对象是它被复制/移动的对象,这也是一个临时的.我们不是你突出显示的情况,因为操作数`test()`不是对象的名称. (3认同)