Как использовать noexcept в C++ или как это работает?

Я не могу понять использование и назначение ключевого слова noexcept в С++ 11/14. Я так понимаю, что это сигнатура функции, которая не выдает exceptions. Но действительно ли это работает?

Посмотрите на этот код ниже:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void seev (vector<int> &v) noexcept;
void seev (vector<int> &v) noexcept
{
    for (int i=0;i<10;++i)
    {
        cout<<v.at(i)<<' ';
    }
}
int main()
{
    vector<int> v {1,2,3,4,5};
    seev(v);
    return 0;
}

Приведенный выше код обязательно выдаст ошибку out_of_range exception. Так что использование noexcept здесь бесполезно, или нет?

Мои запросы:

  1. Как работает noexcept?

  2. Как это используется?

  3. Что throw() не смог сделать из того, что смог noexcept?


person Ankit Acharya    schedule 19.10.2015    source источник
comment
Возможный дубликат Когда мне действительно следует использовать noexcept?   -  person m.s.    schedule 19.10.2015
comment
Полезную информацию можно найти здесь и особенно в этот ответ.   -  person Brandlingo    schedule 19.10.2015


Ответы (3)


Спецификация noexcept для функции — это просто метод, с помощью которого программист сообщает компилятору, должна ли функция генерировать исключения.

Компилятор может использовать эту информацию для включения определенных оптимизаций функций, не вызывающих исключения, а также для включения оператора noexcept, который может проверять во время компиляции, объявлено ли конкретное выражение для создания каких-либо исключений.

Например, такие контейнеры, как std::vector, будут перемещать свои элементы, если конструктор перемещения элементов имеет значение noexcept, и копировать в противном случае (за исключением случаев, когда конструктор копирования недоступен, а конструктор перемещения, потенциально вызывающий исключение, доступен, и в этом случае строгая гарантия исключения отменяется).

noexcept — это улучшенная версия throw(), которая устарела в C++11. В отличие от throw(), noexcept не будет вызывать std::unexpected и может раскручивать стек, а может и не раскручивать его, что потенциально позволяет компилятору реализовать noexcept без накладных расходов во время выполнения throw().

Для получения более подробной информации, пожалуйста, посетите следующие веб-сайты

Изменить: пример исходного кода для иллюстрации вышеуказанных пунктов.

// whether foo is declared noexcept depends on if the expression
// T() will throw any exceptions, check in compile time
template <class T>
void foo() noexcept(noexcept(T())) {     
}

void bar() noexcept(true) {    
}

void baz() noexcept {
    throw 42;     
}  // noexcept is the same as noexcept(true)

int main() 
{
    foo<int>();  // noexcept(noexcept(int())) => noexcept(true), so this is fine

    bar();  // fine
    baz();  // compiles, but at runtime this calls std::terminate
}
person Validus Oculus    schedule 19.10.2015
comment
Я отредактировал свой ответ и добавил пример исходного кода. Я не писал это, но это так легко понять, но очень поучительно. Я надеюсь, что это будет полезно для вас. - person Validus Oculus; 19.10.2015

Я публикую 2 фрагмента кода, чтобы объяснить вашу проблему:

Код 1:

#include <iostream>
using namespace std;
void foo() noexcept     // see the noexcept specifier
{
    throw 42;
}
int main()
{
    try
    {
        foo();
    }
    catch(...)
    {
        cerr<<"exception caught\n";
    }
    return 0;
}

Здесь вывод будет: -

terminate called after throwing an instance of 'int'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Если я удалю noexceptто:

Код 2:

#include <iostream>
using namespace std;
void foo()    // noexcept is eliminated
{
    throw 42;
}
int main()
{
    try
    {
        foo();
    }
    catch(...)
    {
        cerr<<"exception caught\n";
    }
    return 0;
}

Вывод будет: -

exception caught

Поскольку foo был подписан как noexcept, следовательно, был вызван terminate.

Наследующие конструкторы и неявно объявленные конструкторы по умолчанию, конструкторы копирования, конструкторы перемещения, деструкторы, операторы копирования-присваивания и операторы перемещения-присваивания по умолчанию имеют noexcept(true), если только они не требуются для вызова функции noexcept(false), и в этом случае эти функции noexcept(false).

Вы также можете написать такие строки, как:

cout << boolalpha << noexcept(foo);   // here noexcept acts as 
                                     // an operator instead of a specifier

Приведенная выше строка проверяет, выдаст ли foo exception или нет. Если он выбросит, то возвращаемое значение будет true, иначе false.

Подробнее об этом можно прочитать здесь: http://scottmeyers.blogspot.dk/2014/03/declare-functions-noexcept-whenever.html

person CppNITR    schedule 19.10.2015

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

Весь диапазон спецификаторов throw() был удален, поскольку спецификаторы исключений были менее оптимальными в C++, см.: Разница между спецификатором throw() C++03 C++11 noexcept

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

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

noexcept также является оператором, который может оценивать выражение и возвращать значение, может ли это выражение вызвать исключение или нет, в соответствии с § 5.3.7.

5.3.7 оператор noexcept [выражение.unary.noexcept]

1 Оператор noexcept определяет, может ли оценка его операнда, который является невычисленным операндом (раздел 5), вызвать исключение (15.1). noexcept-выражение: noexcept (выражение)

2 Результатом операции noexcept является константа типа bool и значение rvalue.

3 Результат оператора noexcept будет ложным, если в потенциально оцениваемом контексте выражение будет содержать

— потенциально вычисляемый вызов функции, функции-члена, указателя на функцию или указателя на функцию-член, который не имеет негенерирующей спецификации исключения (15.4), если вызов не является константным выражением (5.19),
— потенциально оцениваемое выражение throw (15.1),
— потенциально оцениваемое выражение dynamic_cast dynamic_cast(v), где T — ссылочный тип, требующий проверки во время выполнения (5.2.7), или
— потенциально вычисляемое выражение typeid (5.2.8), примененное к выражению glvalue, тип которого является типом полиморфного класса (10.3).
В противном случае результат будет истинным.

Я не могу объяснить возможные оптимизации, а также Скотт Мейерс: http://aristeia.com/EC++11-14/noexcept%202014-03-31.pdf из его сообщения в блоге: Объявлять функции noexcept всякий раз, когда это возможно?

Разница между раскручиванием стека вызовов и, возможно, его раскручиванием оказывает удивительно большое влияние на генерацию кода. В функции без исключений оптимизаторам не нужно поддерживать стек времени выполнения в неразматываемом состоянии, если исключение будет распространяться за пределы функции, а также они не должны гарантировать, что объекты в функции без исключений уничтожаются в порядке, обратном построению, если исключение покидает функцию. . В результате появляется больше возможностей для оптимизации не только внутри тела функции noexcept, но и на сайтах, где эта функция вызывается. Такая гибкость присутствует только для функций noexcept. В функциях со спецификацией исключения «throw()» его нет, как и в функциях вообще без спецификации исключения.

person Tommy Andersen    schedule 19.10.2015
comment
тогда это кажется бесполезным - person Anwesha; 19.10.2015
comment
Компилятор может выполнить оптимизацию, если присутствует noexcept, что в противном случае дало бы недопустимый код. Это касается раскручивания стека в случае исключения. - person Brandlingo; 19.10.2015
comment
это не бесполезно. Компиляторы могут использовать noexcept и создавать код, оптимизированный для узлов. Обычно код внутренне ведет себя по-разному, когда нет или нет кроме слова. - person DawidPi; 19.10.2015
comment
не могли бы вы уточнить @DawidPi - person Ankit Acharya; 19.10.2015
comment
какие оптимизации @TommyA? - person Ankit Acharya; 19.10.2015
comment
Привет, @AnkitAcharya. Я знал, что не так давно читал пост Скотта Мейерса по этому поводу, мне удалось его найти, и я процитировал его сейчас :) должен довольно хорошо описать, как это может повлиять на оптимизацию. - person Tommy Andersen; 19.10.2015
comment
@AnkitAcharya Нет, как минимум, это четкое заявление разработчика о намерениях пользователей относительно API и того, что он гарантирует, и, помимо этого, он также может включать оптимизацию, в частности, перемещение вместо копирования там, где существует noexcept конструктор перемещения. Так что это далеко не бесполезно. - person underscore_d; 06.10.2018