Переход на C ++ 11, где деструкторы неявно объявляются с noexcept

В C ++ 11 деструктор без какой-либо спецификации исключений неявно объявляется с noexcept, который является изменением от C ++ 03. Таким образом, код, который раньше вызывал деструкторы в C ++ 03, все равно прекрасно компилируется в C ++ 11, но будет зависать во время выполнения, когда он попытается выбросить из такого деструктора.

Поскольку в таком коде нет ошибки времени компиляции, как его можно безопасно перевести на C ++ 11, если не считать объявления всех существующих деструкторов в базе кода noexcept(false), который был бы действительно чрезмерно многословным и навязчивым, или проверял каждого деструктора на предмет потенциального броска, что было бы очень трудоемким и подверженным ошибкам, или перехватывал и исправлял все сбои во время выполнения, что никогда не гарантировало бы что все такие случаи найдены?

10

Решение

Обратите внимание, что правила не так уж жестоки. Деструктор будет только неявно noexcept если бы неявно объявленный деструктор был бы. Таким образом, маркировка по крайней мере одного базового класса или типа элемента noexcept (false) отравит noexceptсущность всей иерархии / совокупности.

#include <type_traits>

struct bad_guy
{
~bad_guy() noexcept(false) { throw -1; }
};

static_assert(!std::is_nothrow_destructible<bad_guy>::value,
"It was declared like that");

struct composing
{
bad_guy member;
};

static_assert(!std::is_nothrow_destructible<composing>::value,
"The implicity declared d'tor is not noexcept if a member's"" d'tor is not");

struct inheriting : bad_guy
{
~inheriting() { }
};

static_assert(!std::is_nothrow_destructible<inheriting>::value,
"The d'tor is not implicitly noexcept if an implicitly"" declared d'tor wouldn't be.  An implicitly declared d'tor"" is not noexcept if a base d'tor is not.");

struct problematic
{
~problematic() { bad_guy {}; }
};

static_assert(std::is_nothrow_destructible<problematic>::value,
"This is the (only) case you'll have to look for.");

Тем не менее, я согласен с Крис Бек что рано или поздно ты должен избавиться от метательных деструкторов. Они также могут заставить вашу программу на C ++ 98 взорваться в самые неудобные времена.

6

Другие решения

Как 5gon12eder упомянули, что существуют определенные правила, которые приводят к тому, что деструктор без спецификации исключения неявно объявляется как noexcept или же noexcept(false), Если ваш деструктор может выдать, и вы оставите его на усмотрение компилятора, чтобы решить его спецификацию исключений, вы будете играть в рулетку, потому что вы полагаетесь на решение компилятора, на которое влияют предки и члены класса, а также их предки и члены рекурсивно , который слишком сложен для отслеживания и может быть изменен в ходе развития вашего кода. Следовательно, при определении деструктора с телом, которое может генерировать, и без спецификации исключения, он должен быть явно объявлен как noexcept(false), С другой стороны, если вы уверены, что тело не может бросить, вы можете объявить это noexcept чтобы быть более явным и помочь компилятору оптимизировать, но будьте осторожны, если вы решите это сделать, потому что, если деструктор любого члена / предка вашего класса решит выбросить, ваш код будет прерван во время выполнения.

Обратите внимание, что любые неявно определенные деструкторы или деструкторы с пустыми телами не создают проблем. Они только неявно noexcept если все деструкторы всех членов и предков noexcept также.

Поэтому лучший способ продолжить переход — найти все деструкторы с непустыми телами и без спецификаций исключений и объявить каждый из них, который может выдать с noexcept(false), Обратите внимание, что вам нужно только проверить тело деструктора — любые немедленные броски, которые он делает, или любые броски, сделанные функциями, которые он вызывает, рекурсивно. Нет необходимости проверять деструкторы с пустыми телами, деструкторы с существующей спецификацией исключений или любые неявно определенные деструкторы. На практике не будет так много тех, кто останется проверенным, поскольку распространенное использование для них просто освобождение ресурсов.

Поскольку я отвечаю самому себе, это именно то, что я в итоге сделал в моем случае, и в конце концов это было не так больно.

4

Однажды я сам прошел через эту же дилемму.

По сути, я пришел к выводу, что принять тот факт, что эти деструкторы бросают и просто живут с последствиями этого, обычно гораздо хуже, чем переживать боль, заставляя их не бросать.

Причина в том, что вы рискуете еще более нестабильными и непредсказуемыми состояниями, когда у вас есть метательные деструкторы.

В качестве примера я однажды работал над проектом, в котором по разным причинам некоторые разработчики использовали исключения для управления потоком данных в какой-то части проекта, и он работал отлично в течение многих лет. Позже кто-то заметил, что в другой части проекта иногда клиенту не удавалось отправить некоторые сетевые сообщения, которые он должен отправить, поэтому он создал объект RAII, который отправлял сообщения в своем деструкторе. Иногда сетевое взаимодействие выдает исключение, так что этот деструктор RAII выбрасывает, но кого это волнует? У него нет памяти для очистки, так что это не утечка.

И это будет работать нормально в 99% случаев, кроме случаев, когда путь управления потоком исключений пересекает сеть, что также вызывает исключение. И затем, у вас есть два живых исключения, которые разматываются одновременно, так что «блин, вы мертвы», в бессмертных словах C ++ FAQ.

Честно говоря, я бы предпочел, чтобы программа мгновенно закрывалась при броске деструктора, поэтому мы можем поговорить с тем, кто написал метатель-деструктор, а не пытаться поддерживать программу с преднамеренно выбрасывающими деструкторами, и это, похоже, является консенсусом комитета / сообщества , Таким образом, они внесли это серьезное изменение, чтобы помочь вам утверждать, что ваши деструкторы хороши, а не бросают. Может быть много работы, если в вашей унаследованной кодовой базе много хаков, но если вы хотите продолжать разрабатывать и поддерживать ее, по крайней мере, в стандарте C ++ 11, вам, вероятно, лучше выполнить работу по очистке до деструкторов.

Нижняя линия:

Вы правы, вы не можете надеяться гарантировать, что вы найдете все возможные случаи метания деструктора. Так что, вероятно, будут некоторые сценарии, в которых когда ваш код компилируется на C ++ 11, он потерпит крах в тех случаях, когда он не соответствует стандарту C ++ 98. Но в целом, очистка деструкторов и запуск под C ++ 11, вероятно, будет намного более стабильным, чем просто использование деструкторов по старому стандарту.

3
По вопросам рекламы [email protected]