可以rethrow_exception真的抛出相同的异常对象,而不是副本吗?

时间:2013-02-08 15:03:42

标签: c++ c++11 language-lawyer

在研究exception_ptr的作用时,C ++ 11标准说(18.8.5 / 7):

  

对引用同一异常对象的exception_ptr对象使用rethrow_exception不应引入数据争用。 [注意:如果rethrow_exception重新抛出相同的异常对象(而不是副本),对该重新抛出的异常对象的并发访问可能会引入数据争用......

我找不到这种奇怪的情况"注意"适用,因为所描述的rethrow_exception的效果是"抛出:p所指的异常对象"但是15.1 / 3,描述了投掷过程的一般异常,即抛出异常复制 - 初始化一个临时对象,称为异常对象。"

奇怪的说明意味着rethrow_exception会跳过此复制初始化。但这真的有可能吗?

3 个答案:

答案 0 :(得分:3)

当您说throw x;时,异常对象与x具有相同的类型,但它是副本。

当你说std::rethrow_exception(p);时,异常对象是指针引用的实际对象,不再进行复制。

因此,有几个线程同时重新抛出相同的异常指针(你可以复制它!),然后它们都引用了相同的对象。

答案 1 :(得分:3)

是的,它似乎是标准的缺陷。对于重新抛出的throw-expression,即没有操作数的throw;,15.1p8说:

  

没有操作数的 throw-expression 重新抛出当前处理的异常。使用现有异常对象重新激活该异常;没有创建新的异常对象。 [...]

那是:

#include <exception>
#include <cassert>
int main() {
   std::exception *p = nullptr;
   try {
      try {
         throw std::exception();
      } catch(std::exception &ex) {
         p = &ex;
         throw;
      }
   } catch(std::exception &ex) {
      assert(p == &ex);
   }
}

如果current_exception的实现复制了当前处理的异常对象,则无法判断rethrow_exception是否复制,但如果它引用了异常对象,那么我们可以检查:

#include <exception>
#include <iostream>
int main() {
   std::exception_ptr p;
   try {
      try {
         throw std::exception();
      } catch(...) {
         p = std::current_exception();
         std::cout << (p == std::current_exception()) << ' ';
         std::rethrow_exception(p);
      }
   } catch(...) {
      std::cout << (p == std::current_exception()) << '\n';
   }
}

我在打印1 1时尝试过的每个实现;如果0 0份,则允许current_exception; 0 1显然是不可能的,而当前状态的标准似乎需要1 0。使用类似于15.1p8的语言澄清18.8.5p10的修复,允许或强制rethrow_exception不复制exception_ptr指向的异常对象。

标准中的大多数投掷:规范只是命名一种类型(投掷:bad_alloc )或使用不定冠词(抛出:异常类型...... );使用定冠词的唯一其他例外规范是future::getshared_future::get,所以任何解决方案都应该解决这些问题。

答案 2 :(得分:2)

是的,这是可能的。异常处理机制已经有一个最初抛出的对象的副本,在私有内存中被squirleled。通常,exception_ptr实现为智能指针,用于管理该副本的引用计数。

对于一般要求,如果特定要求与一般要求冲突,则具体要求获胜。