noexcept c что это
Как работает noexcept в c++?
Что делает спецификация noexcept? Я думал, что она не позволит функции кинуть исключение, но это не так, функция:
компилируется, её вызов спокойно падает, значит исключение вылетело. noexcept ничего не изменил.
3 ответа 3
Спецификатор noexcept дает гарантию времени компиляции, что из функции не будет выброшено исключение. Однако такая гарантия достигается эквивалентно оборачиванию тела функции в блок try. catch с вызовом ::std::terminate :
Определить, что в теле функции исключений действительно не выбрасывается и соптимизировать их перехват компиляторы могут только в самых простых случаях. Идея была в том, что оптимизацию сможет осуществить вызывающий код. Но на практике это все не работает и контроля над исключениями во время компиляции, особенно после выкидывания спецификаторов исключений, в с++ фактически нет.
Этим вы как автор гарантируете, что ваша функция не генерирует исключений, так что компилятор может, полагаясь на это, выполнять ряд оптимизаций.
Всё ещё ищете ответ? Посмотрите другие вопросы с метками c++ исключения c++11 или задайте свой вопрос.
Связанные
Похожие
Подписаться на ленту
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
дизайн сайта / логотип © 2021 Stack Exchange Inc; материалы пользователей предоставляются на условиях лицензии cc by-sa. rev 2021.12.16.41042
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Ключевое слово noexcept
Определить, в введенном предложении слово, которое состоит из тех же букв, что и заданное ключевое слово.
Ввести предложения и ключевое слово. Определить, в введенном предложении слово, которое состоит из.
Ключевое слово this
Можно какой-нибудь простой пример, чтобы понять зачем это нужно?
Ключевое слово this
В данный момент читаю книгу по Java, но и по С++ это тоже встречалось, хотя описание было не сильно.
Ключевое слово try
Вот вчера печатал программу и нечаяно поставил вместо слова tru слово try и оно выделелось жырным.
Не совсем так. Если все исключения, которые бросаются из вложенных методов, перехватываются в текущем, то текущий метод остаётся noexcept’ом, т.к. наверх из него ничего не должно вылетать
Добавлено через 46 секунд
А если не перехватываются, значит не может быть noexcept-ом Понял
А как насчет первого вопроса? Значит нужно писать везде откуда не вылетит исключение, что бы сгенерировалось меньше коде?
Иногда очень важно что бы публичный метод не бросал исключений (внешний интерфейс библиотеки или деструктор). Что бы он таким был необходимо либо использовать только noexept методы или функции, либо перехватывать всё что они бросают.
Т.е. то что метод noexept, позволяет делать использующие его методы тоже noexept.
Это как с const. Начинаешь чертыхаться что не можешь сделать метод const из-за того что какой то умник в стародавние времена не стал делать в интерфейсе константным какой нибудь getSize().
Noexcept это не гарантия того, что функция не кинет исключение. Это гарантия того, что если функция кинет исключение, программа сделает харакири чтобы избежать позора (немедленно вызовет std::terminate). Поэтому, код для ловли исключения и совершения харакири все равно будет сгенерирован.
Тем не менее да, по возможности надо везде втыкать noexcept. Потому что, скажем, std::vector воротит нос от перемещающего конструктора без no-throw гарантии (нужно для выполнения гарантии «в случае сбоя insert, вектор останется в корректном состоянии»). Что может весьма сильно сказаться на скорости работы этого самого вектора (перемещающий конструктор может быть на порядки быстрее копирующего).
Скажем так, современные компиляторы используют zero cost exceptions. Т.е. исполняемый код будет одинаковый независимо от использования noexcept. Но для такой реализации компилятор генерирует довольно большие портянки со специальными данными. В случае noexcept размер этой портянки будет поменьше
Поэтому тут не то чтобы «нужно». Как уже упомянул коллега, чем-то это напоминает const. В о многих случаях его «не нужно» писать. Но если написать, то текст программы станет более удобным для чтения, появится некоторый контроль за ошибками со стороны компилятора, в некоторых случаях компилятор отработает более эффективно и т.п. Т.е. «нужно» это не как обязаловка, а как культура программирования
Спецификатор времени компиляции noexcept в C++11
Уменьшение размера бинарного файла
Давайте рассмотрим следующий пример, в котором комментариями рассказывается, что за код генерируется компилятором:
Многовато кода генерируется компилятором, не правда ли? Именно из-за такого разбухания кода, в некоторых крупных корпорациях (не будем тыкать пальцем в Google) при разработке на С++ запрещено использование исключений. Еще одним примером могут послужить правила разработки для GCC начиная с версии 4.8 (да, GCC теперь разрабатывается с использованием С++, см изменения для 4.8).
Давайте модифицируем класс my_class, чтобы он использовал noexcept и посмотрим что может генерировать компилятор:
Заметьте, что std::cerr больше не используется, а следовательно компилятор сможет убрать очень большой кусок кода. Но даже без этого, количество сгенерированного кода стало меньше.
К несчастью, мы рассматривали идеальный компилятор. На практике все может оказаться не так здорово: фича достаточно новая, не все компиляторы умеют хорошо ее использовать при оптимизациях.
Так же, некоторые компиляторы иногда и без noexcept могут определить, будут класс или метод кидать исключения.
Сравнивая первые две строчки получаем ожидаемое улучшение размера в случае идеального компилятора.
Третья, четвертая и пятая строчки показывают, что компилятор и без noexcept смог соптимизировать до нашего второго варианта, и лишь не смог выкинуть лишние объявления, используемые в заголовочном файле iostream.
Итого: выигрыш в размере в 23.9% после добавления трех noexcept.
Ускорение работы стандартных алгоритмов
Для многих людей, использующих С++11 может стать большой неожиданностью данная часть стандарта N3050 (на данный момент этот стандарт реализован не во всех ведущих компиляторах). Если в кратце, в ней говорится, что стандартные алгоритмы и контейнеры не должны использовать move assignment и move construction если эти методы могут кидать исключения. Чем это может грозить рассмотрим на примере:
Если методы 1 и 3 не пометить noexcept, то стандартные контейнеры будут использовать более медленные методы 2 и 4. Вследствие этого работа с контейнерами std::vector и std::deque может замедлиться на пару порядоков. При том, данное замедление коснется и всех типов наследуемых или использующих move_fast_copy_slow в качестве члена.
Совместимость
На момент написания данной статьи не все ведущие компиляторы поддерживают noexcept. Используйте макрос BOOST_NOEXCEPT из библиотеки boost вместо noexcept для переносимости кода.
Подводные камни
По стандарту, noexcept не является частью типа функции, однако при использовании виртуальных функций все перегруженные функции должны иметь такую же либо более строгую спецификацию исключений. То есть следующий код не соберется:
Будет ошибка типа «error: looser throw specifier for ‘virtual void derived::foo()’».
Так что будьте готовы к тому, что если вы автор библиотеки и добавили спецификацию noexcept к фиртуальной функции, то у пользователей вашей библиотеки перестанет собираться код.
Так же приготовьтесь к тому, что ваши классы исключений c перегруженным методом what() и наследуемые от стандартных, могут не компилироваться если они не помечены noexcept:
noexcept (C++)
C++ 11: Указывает, может ли функция создавать исключения.
Синтаксис
noexcept-specifier :
noexcept
noexcept-expression
throw ( )
noexcept-expression :
noexcept ( constant-expression )
Параметры
Комментарии
noexcept-expression Тип — это разновидность noexcept-expression : суффикс для объявления функции, представляющий набор типов, которые могут сопоставляться обработчиком исключений для любого исключения, которое выходит из функции. Унарный условный оператор noexcept(constant_expression) при constant_expression выдает true и его безусловном синониме noexcept Укажите, что набор потенциальных типов исключений, которые могут выйти из функции, пуст. Это значит, что функция никогда не создает исключение и никогда не позволяет распространять исключение за пределы области действия. Оператор noexcept(constant_expression) при constant_expression выдает false или отсутствие спецификации исключения (кроме деструктора или функции освобождения) указывает, что набор возможных исключений, которые могут выйти из функции, — это набор всех типов.
Пример
Функция шаблона, которая копирует свой аргумент, может быть объявлена noexcept в условии, что копируемый объект является обычным старым типом данных (Pod). Такая функция может быть объявлена следующим образом:
20.9 – Спецификации исключений и noexcept
В C++ все функции классифицируются как не выбрасывающие исключения (не генерируют исключения) или потенциально выбрасывающие исключения (могут генерировать исключения).
Рассмотрим следующее объявление функции:
Глядя на типовое объявление функции, невозможно определить, может ли функция выбросить исключение или нет. Хотя комментарии могут помочь определить, генерирует ли функция исключения или нет (и если да, то какие исключения), документация может устареть, а компилятор не использует комментарии.
Спецификации исключений – это языковой механизм, который изначально был разработан как часть спецификации функций для документирования того, какие исключения может вызывать функция. Хотя большая часть спецификаций исключений теперь устарела или удалена, в качестве замены была добавлена другая полезная спецификация исключений, которую мы и рассмотрим в этом уроке.
Спецификатор noexcept
Спецификатор noexcept определяет функцию как не вызывающую исключения и используется в объявлении функции справа от списка параметров:
Как и функции, отличающиеся только своими возвращаемыми значениями, функции, отличающиеся только спецификацией исключения, так же не могут быть перегружены.
Спецификатор noexcept с логическим параметром
Какие функции не выбрасывают исключения, а какие потенциально выбрасывают исключения
Функции, которые по умолчанию не выбрасывают исключения:
Однако если какая-либо из перечисленных функций вызывает (явно или неявно) другую функцию, которая потенциально выбрасывает исключения, то указанная функция также будет рассматриваться как потенциально выбрасывающая исключения. Например, если в классе есть член данных с конструктором, потенциально выбрасывающим исключения, то конструкторы данного класса также будут рассматриваться как потенциально выбрасывающие исключения. В качестве другого примера, если оператор присваивания копированием вызывает оператор присваивания, потенциально выбрасывающий исключения, то этот оператор присваивания копированием также будет считаться бросать потенциально выбрасывающим исключения.
Лучшая практика
Если вы хотите, чтобы какие-либо из перечисленных выше функций были не выбрасывающими исключения, явно пометьте их как noexcept (даже если они заданы таким образом по умолчанию), чтобы они случайно не стали потенциально выбрасывающими исключения.
По умолчанию потенциально выбрасывающими исключения могут быть:
Оператор noexcept
Оператор noexcept может использоваться для условного выполнения кода в зависимости от того, является ли его аргумент потенциально выбрасывающим исключения или нет. Это необходимо для выполнения определенных гарантий безопасности исключений, о которых мы поговорим в следующем разделе.
Гарантии безопасности исключений
Гарантия безопасности исключений – это договоренность о том, как функции или классы будут вести себя в случае возникновения исключения. Существует четыре уровня безопасности исключений:
Давайте рассмотрим гарантию без выбросов исключений / без сбоев более подробно:
Гарантия отсутствия выбросов исключений: если функция дает сбой, она не вызывает исключения. Вместо этого она вернет код ошибки или проигнорирует проблему. Гарантии отсутствия выбросов исключений требуются во время раскручивания стека, когда исключение уже обрабатывается; например, все деструкторы должны иметь гарантию отсутствия выбросов исключений (как и любые функции, вызываемые этими деструкторами). Примеры кода, который должен быть не выбрасывающим исключения:
Гарантия отсутствия сбоев: функция всегда будет успешной в том, что она пытается сделать (и, таким образом, никогда не будет необходимости генерировать исключение, поэтому отсутствие сбоев – это немного более строгая форма отсутствия выбросов исключений). Примеры кода, который должен быть без сбоев:
Когда использовать noexcept
Тот факт, что ваш код не генерирует явно никаких исключений, не означает, что вы должны начать использовать noexcept везде, где только можно. По умолчанию большинство функций потенциально выбрасывают исключения, поэтому, если ваша функция вызывает другие функции, есть большая вероятность, что она вызовет функцию, которая потенциально выбрасывает исключения, и, следовательно, так же станет потенциально выбрасывающей исключения.
Лучшая практика
Используйте спецификатор noexcept в случаях, когда хотите показать гарантию отсутствия сбоев и выбросов исключений.
Лучшая практика
Почему полезно помечать функции как не выбрасывающие исключения
Есть несколько веских причин отмечать функции как не выбрасывающие исключения:
Динамические спецификации исключений
Дополнительные материалы
До C++11 и до C++17 вместо noexcept использовались динамические спецификации исключений. Синтаксис динамических спецификаций исключений использует ключевое слово throw для перечисления типов исключений, которые функция может прямо или косвенно генерировать:
Из-за таких факторов, как неполные реализации компиляторов, некоторая несовместимость с шаблонами функций, распространенное недопонимание того, как они работают, и тот факт, что стандартная библиотека в основном их не использовала, динамические спецификации исключений были объявлены устаревшими в C++11 и удалены из языка в C++17 и C++20. Для получения более подробной информации смотрите этот документ.