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

时间:2012-01-06 16:13:15

标签: 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; }
}

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

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

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

1 个答案:

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