Как std :: runtime_error :: runtime_error (const std :: string &) может удовлетворить требование std :: исключения для throw ()? - PullRequest
17 голосов
/ 28 июля 2011

std::exception требует, чтобы его конструктор был throw().Тем не менее, std::runtime_error принимает std::string в качестве аргумента, который указывает, что он хранит std::string где-то.Следовательно, назначение или копирование должно происходить где-то.А для std::string это не операция nothrow.

Как тогда runtime_error::runtime_error соответствует throw()?

(Для контекста я реализую тип исключения, ихочу сохранить несколько std::string с сайта вызовов, и я хочу сделать это правильно ...)

Ответы [ 2 ]

9 голосов
/ 28 июля 2011

( Вот то же самое в тестовом сценарии с минимальным набором символов.)


runtime_error::runtime_error(string const&) не нужно встречать throw().

Он не наследуется и не переопределяет exception::exception(), и к тому времени, когда вызывается конструктор копирования string, exception::exception() завершен.

Если при копировании string возникнет исключение, это приведет к раскручиванию runtime_error::runtime_error(string const&), а затем, я полагаю, вызовет exception::~exception().


Трудно прямо показать, что не требуется, чтобы производный ctor соответствовал спецификатору исключения базового ctor, но это настоятельно подразумевается в следующем отрывке (который описывает, как вызывается деструктор базы, вместо передачи исключения в базовый конструктор):

[2003: 15.2/2] Объект, который частично построен или частично разрушенные будут казнены деструкторы для всех полностью построенные подобъекты, то есть для подобъектов, для которых конструктор завершил выполнение, а деструктор еще не начало выполнения. Должен конструктор для элемента автоматического массив выдает исключение, только сконструированные элементы этого массива будет уничтожен Если объект или массив был выделен в new-выражение, соответствующая функция освобождения (3.7.3.2, 5.3.4, 12.5), если таковой имеется, вызывается для освобождения памяти, занимаемой объектом.

Единственный отрывок, который даже приближается к сценарию, который вы предполагали (и который я изначально предполагал), следующий:

[2003: 15.4/3] Если виртуальная функция имеет спецификацию исключений, все объявления, включая определение любой функции, которая переопределяет, что виртуальная функция в любом производном классе должна позволять только исключения, которые разрешены спецификацией исключений базы класс виртуальной функции.

Но, очевидно, exception::exception() не является виртуальной функцией, и, очевидно, runtime_error::runtime_error(string const&) не отменяет ее.

(Обратите внимание, что этот сценарий будет применяться для виртуального деструктора; соответственно, вы можете видеть, что в libstdc ++ runtime_error::~runtime_error() равно throw()).

6 голосов
/ 28 июля 2011

Обновление, 2015:

Тем не менее std::runtime_error принимает std::string в качестве аргумента, который указывает, что он хранит std::string где-то. Следовательно, назначение или копирование должно происходить где-то. А для std::string это не операция noexcept.

runtime_errorlogic_error) требуются только для принятия аргумента типа std::string const &. Они не обязаны его копировать.

Используйте эти перегрузки на свой страх и риск. LLVM libc ++ не предоставляет хранилище.

С другой стороны, GNU libstdc ++ осторожно на цыпочках избегает нехватки памяти. Он копирует содержимое строки, но в область хранения исключений, а не в новый std::string.

Даже тогда он добавляет перегрузку std::string&& и использует friend ship для принятия внутреннего буфера аргумента std::string, переданного rvalue, для экономии места хранения исключений.

Так вот ваш реальный ответ: "Очень осторожно, если вообще."

Вы можете использовать щедрость GCC, используя std::runtime_error s как члены вашего собственного класса исключений, сохраняя по одной строке каждый. Это все равно будет бесполезно для Clang.


Оригинальный ответ, 2011 г. Это все еще верно:

Исключение при разматывании стека вызывает terminate.

Но создание объекта, который должен быть брошен, не является частью разматывания, и рассматривается как код, предшествующий выражению throw.

Если std::runtime_error::runtime_error( std::string const & ) выдает std::bad_alloc, исключение runtime_error теряется (оно никогда не существовало) и вместо него обрабатывается bad_alloc.

Демонстрация: http://ideone.com/QYPj3

Что касается вашего собственного класса, хранящего std::string s с сайта вызовов, вы должны следовать §18.8.1 / 2:

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

Это необходимо, поскольку при копировании из стека в хранилище исключений потока чувствительно к исключениям. §15.1 / 7:

Если механизм обработки исключений, после завершения вычисления выражения, которое должно быть выброшено, но до того, как исключение было перехвачено, вызывает функцию, которая завершается через исключение, вызывается std :: terminate (15.5.1).

Итак, вы должны использовать shared_ptr< std::string > или что-то подобное для дезинфекции копий после первого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...