10

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

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

+1

Интересно, что головная версия gcc предоставляет предупреждение для этого через '-Wterminate'. Я не вижу аналогичного варианта для clang. –

+2

Как я всегда говорил, спецификация исключений на языке С ++ - это шутка. Извините, нет конструктивного asnwer, но я разделяю вашу боль. – SergeyA

+0

@ShafikYaghmour Это интересно, но мой GCC 5.2.0 не распознает этот вариант. У вас есть ссылка на него? – 5gon12eder

ответ

2

Я сам прошел эту же дилемму.

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

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

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

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

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

Bottom Line:

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

+0

Нет единого мнения, что бросать dtors - плохая идея. Несколько известных людей не консенсус. – curiousguy

+0

Хорошо, может быть, не консенсус, но, безусловно, большинство. Думаю, я не могу говорить за всех, но те, кто не против бросать dtors, настолько редки, что я никогда не встречал кого-то, кто думает, что это не должно быть против стандартов кодирования, и я никогда не видел библиотеки или программным обеспечением, которое основано на метании dtors. Я видел только кучу устаревшего кода приложения, который делает это. –

+0

Впечатление от консенсуса всегда преувеличено групповым мышлением и запугиванием. Аргументы против бросания дторов очень слабы.Дискуссия сводится к тому, что «бросание dtors вызывает проблемы» (что также верно для MI), «бросание dtors приводит к завершению» (что также верно для 'throw()'), а в «я не хочу подумайте об этом ». Никто не хочет столкнуться с проблемой, которую могут бросить dtors, потому что они не могут выполнять свою работу, как и любая другая функция. Необходимо решить проблему обработки ошибок в dtors. – curiousguy

6

Обратите внимание, что правила на самом деле не являются жестокими. Деструктор будет только неявно 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."); 

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

+0

Это было очень полезно, спасибо! Так как ответа не было, я, в конечном итоге, ответил сам; Интересно, согласны ли вы с моей мыслью. – dragonroot

3

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

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

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

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

+0

Я думаю, что это хороший подход. Когда я пишу код, мне всегда нравится быть явным и объявлять деструкторы как «noexcept», даже если это не является строго необходимым, потому что это будет по умолчанию в любом случае. Хотелось бы отметить, что вы не «оставляете решение компилятору», если вы не являетесь явным, - эти правила написаны в стандарте, а компилятор имеет нулевую силу для их применения. Конечно, для ошибок компилятора. – 5gon12eder

+0

Мне действительно пришлось пересмотреть часть, где я рекомендовал маркировать всех деструкторов неметающими телами 'noexcept', так как их члены и предки все еще могут бросать, и это приведет к отмене, если деструктор отмечен как« noexcept'. Что касается правил, да, они очень явные - просто трудно отследить человека. – dragonroot

 Смежные вопросы

  • Нет связанных вопросов^_^