2013-02-08 1 views
7

При рассмотрении того, что exception_ptr делает, стандарт C++ 11 говорит (18.8.5/7), что:Может rethrow_exception действительно бросать один и тот же объект исключения, а не копию?

Использование rethrow_exception на exception_ptr объектов, которые относятся к одному объекту исключения не должны вводить гонки данных. [Примечание: если rethrow_exception rethrows один и тот же объект исключения (а не копии), одновременный доступ к этому объекту исключения могут вызваны повторно ввести гонки данных ...

Я не нашел случай, когда это странно «Примечание «применяется, поскольку описанный эффект rethrow_exception -« Throws: объект исключения, к которому относится p », но 15.1/3, описывающий общие требования к металированию исключения, которые« бросают копию исключения »инициализируют временный объект, называемый объектом исключения. "

Странное примечание подразумевает, что rethrow_exception пропускает эту инициализацию копии. Но действительно ли это возможно?

+0

Может быть, это просто движение? –

+0

Yup, 'std :: rethrow_exception' не могут быть реализованы с использованием выражения' throw x; '. (Но это похоже на 'throw;'.) – aschepler

ответ

3

Да, это похоже на недостаток стандарта. Для вбрасывание выражения Повторного выбрасывания т.е. throw; без операнда, 15.1p8 говорит:

вбрасывание выражение, без операнда rethrows в данный момент обрабатываются исключением. Исключение повторяется с существующим объектом исключения; новый объект исключения не создается. [...]

То есть:

#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.Исправление было бы для 18.8.5p10 для уточнения с языком, аналогичным 15.1p8, либо разрешающим, либо обязательным rethrow_exception, чтобы не копировать объект исключения, на который указывает exception_ptr.

Большинство Броски: спецификации в стандарте только имя типа (Броски: bad_alloc) или использовать неопределенный артикль (Броски: исключение типа ...); единственными другими спецификациями исключения для использования определенного товара являются статьи future::get и shared_future::get, поэтому любое решение, вероятно, должно адресовать их.

+0

После дальнейших расследований я нашел вопрос о том, что LWG предлагает направить копию в этом случае (выпуск № 1369). Похоже, что они согласились с этим, подтверждая, как, по-видимому, ведут себя реализации. Я считаю, что разъяснение, подобное тому, которое вы предлагаете, имеет смысл. – soulie

2

Да, это возможно. Механизм обработки исключений уже имеет копию объекта, который изначально был выброшен, безвозвратно в секретном хранилище памяти. Как правило, exception_ptr реализован как интеллектуальный указатель, который управляет счетчиком ссылок для этой копии.

Что касается требований общего назначения, то при возникновении особых требований, связанных с общим требованием, выигрывает конкретное требование.

+0

Но заметки AFAIK не являются нормативными, поэтому повторное создание должно создать утвержденную новую копию. – soulie

+0

@twicker - возможно, вы правы, что это неправильно указано. '' 'exception_ptr' изначально было расширением библиотеки, которое требовало расширения поддержки исключений во время выполнения, но никаких изменений языка. Требование новой копии означало бы необходимость хранения указателя на конструктор копии объекта; в то время как это технически осуществимо, гораздо проще просто повторно использовать старый объект. И на практике нет никакой разницы; вы можете написать конструктор копирования, который записывает, вызван ли он, но это в стороне, это не влияет на какой-либо реальный код. –

3

Когда вы говорите throw x;, тогда объект исключения имеет тот же тип, что и x, но является копией.

Когда вы говорите std::rethrow_exception(p);, объект исключения является фактическим объектом, на который указывает указатель, и дальнейшие копии не создаются.

Таким образом, есть несколько потоков одновременно повторно выдать указатель исключений же (которые вы можете копировать!), То все они имеют ссылку на же объекта.

+0

Хорошо, это имеет смысл. Тем не менее, стандарт запутывает, когда описывает поведение rethrow_exception как «Throws: объект исключения, к которому относится p». – soulie

+0

@twicker: Я думаю, это довольно ясно. Всегда существует понятие «объект исключения», а 'std :: current_exception' создает указатель на этот объект. –