Как std:: runtime_error:: runtime_error (const std::string &) отвечает std:: exception требование throw()?
std::exception
требует, чтобы его конструктор был throw()
. Тем не менее std::runtime_error
принимает std::string
как свой аргумент, что указывает на то, что он где-то хранит std::string
. Следовательно, назначение или создание копии должно происходить где-то. А для std::string
это не операция nothrow
.
Как тогда runtime_error::runtime_error
встречается throw()
?
(Для контекста я реализую тип исключения и хочу сохранить несколько std::string
с сайта вызова, и я хочу сделать это правильно...)
Ответы
Ответ 1
(Вот то же самое в минимальной тестовой папке.)
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]
Объект, который частично сконструирован или частично уничтожены, будут уничтожены деструкторы для всех своих полностью построенных подобъектов, т.е. для подобъектов, для которых конструктор завершил выполнение, а деструктор еще не начальное выполнение. Если конструктор элемента автоматического array выдает исключение, только построенные элементы этого массива будут уничтожены. Если объект или массив был выделен в новое выражение, соответствующая функция дезадаптации (3.7.3.2, 5.3.4, 12.5), если таковой имеется, вызывается для освобождения памяти, занимаемой объектом.
Единственный проход, который приближается к сценарию, который вы предположили (и который я изначально предполагал), следующий.
[2003: 15.4/3]
Если виртуальная функция имеет спецификацию исключения, все декларации, включая определение, любой функции, которая переопределяет, что виртуальная функция в любом производном классе допускает исключения, допускаемые спецификацией исключения базы класса.
Но ясно, что exception::exception()
не является виртуальной функцией, и, очевидно, runtime_error::runtime_error(string const&)
не переопределяет его.
(Обратите внимание, что этот сценарий применим для виртуального деструктора, поэтому вы можете видеть, что в libstdС++ runtime_error::~runtime_error()
есть throw()
).
Ответ 2
Обновление, 2015:
Тем не менее std::runtime_error
принимает std::string
как свой аргумент, что указывает на то, что он где-то хранит std::string
. Следовательно, назначение или создание копии должно происходить где-то. И для std::string
, это не операция noexcept
.
runtime_error
(и logic_error
) необходимы только для принятия аргумента типа std::string const &
. Им не требуется копировать его.
Используйте эти перегрузки на свой страх и риск. LLVM libС++ не обеспечивает хранения.
С другой стороны, GNU libstdС++ аккуратно берется, чтобы избежать нехватки памяти. Он копирует содержимое строки, но в пространство хранения исключений, а не в новый std::string
.
Даже тогда он добавляет перегрузку std::string&&
и использует корабль friend
для принятия внутреннего буфера аргумента std::string
, переданного rvalue, для экономии места для хранения исключений.
Итак, чтобы ваш реальный ответ: "Очень осторожно, если вообще".
Вы можете использовать щедрость GCC, используя std::runtime_error
в качестве членов вашего собственного класса исключения, сохраняя по одной строке каждый. Однако это все равно было бы бесполезно для Клана.
Оригинальный ответ, 2011. Это все еще так:
Исключение во время разматывания строк вызывает вызов terminate
.
Но построение объекта, который нужно выбросить, не является частью разматывания и не обрабатывается иначе, чем код перед выражением throw
.
Если std::runtime_error::runtime_error( std::string const & )
throws std::bad_alloc
, исключение runtime_error
теряется (оно никогда не существовало) и вместо него обрабатывается bad_alloc
.
Демонстрация: http://ideone.com/QYPj3
Что касается вашего собственного хранения классов std::string
с сайта вызова, вы должны следовать §18.8.1/2:
Каждый стандартный класс библиотеки T, полученный из исключения класса, должен иметь общедоступный конструктор копирования и общедоступный оператор присваивания копии, который не выходит из исключения.
Это необходимо, потому что копирование из стека в хранилище исключений потоков чувствительно к исключениям. §15.1/7:
Если механизм обработки исключений, после завершения оценки выражаемого выражения, но до того, как исключение поймано, вызывает функцию, которая выходит из исключения, вызывается std:: terminate (15.5.1).
Итак, вы должны использовать shared_ptr< std::string >
или некоторые из них для дезинфекции копий после первого.