【问题标题】:If a thrown exception is always a copy of the exception object, why isn't this copy constructor being invoked?如果抛出的异常始终是异常对象的副本,为什么不调用此复制构造函数?
【发布时间】:2012-01-06 16:13:14
【问题描述】:

斯科特迈耶斯说:

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 的引用,即使通过引用接收异常对象也应该被复制。

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

【问题讨论】:

  • 问题是“哪个是错的:迈耶斯还是我的编译器?”不要仅仅因为你不理解问题而关闭它们。

标签: c++


【解决方案1】:

从语义上来说,Meyers 是正确的:

[C++11: 12.2/1]: 类类型的临时对象在各种上下文中创建:将引用绑定到纯右值(8.5.3),返回纯右值(6.6.3),创建纯右值的转换(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 非限定类型的类对象时,通过将临时对象直接构造到省略的复制/移动的目标中,可以省略复制/移动操作。
  • [..]

【讨论】:

  • 对于 12.8/31,我们实际上是在下一种情况下,“当尚未绑定到引用 (12.2) 的临时类对象将被复制/移动到具有相同cv-unqualified 类型,可以通过将临时对象直接构造到省略的复制/移动的目标中来省略复制/移动操作”。 test() 是临时的,以未指定方式分配的异常对象是要复制/移动的对象,这恰好也是临时的。我们不是在您突出显示的情况下,因为操作数 test() 不是对象的名称。
  • @SteveJessop:是的。此外,我认为有关范围的条件由于“函数调用间接”(由我组成的术语)而失败。
  • @SteveJessop:最后的情况是不是因为accept-by-ref而无关?
  • 您突出显示的位是在void fun() { test foo; throw foo; } 的情况下允许复制省略。所以foo 的作用域只是函数fun(),它不包含最里面的try-block。我不知道为什么它禁止 void fun() { try { test foo; throw foo; } catch (std::bad_alloc &amp;) {}void fun(test foo) { throw foo; } 的复制省略,不过,我不太明白为什么有人会在这些情况下依赖复制的副作用。
  • 嗯,我不会想太多。这个答案是正确的“足够”。 :)
【解决方案2】:

从C++11开始,当一个对象作为异常抛出时,复制到异常对象可能会被省略,如果满足或将满足复制省略的条件,除了source是函数参数,但是由于某些原因编译器没有忽略它,即使对象是由左值指定的,编译器也必须尝试使用​​移动构造函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-18
    • 2019-07-11
    • 2014-08-02
    相关资源
    最近更新 更多