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

Я не могу понять использование и назначение 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 здесь бесполезно, или оно?

Мои запросы:

  • Как работает noexcept?

  • Как он используется?

  • Что throw() не удалось сделать, чтобы noexcept мог?

Ответ 1

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

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

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

noexcept - это улучшенная версия throw(), которая устарела в С++ 11. В отличие от throw(), noexcept не вызывает std:: неожиданный и может или не может размотать стек, что потенциально позволяет компилятору реализовать 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
}

Ответ 2

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

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

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

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

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

5.3.7 оператор noexcept [expr.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 будут уничтожены в обратном порядке построения, если исключение оставит функцию, В результате появляется больше возможностей для оптимизации не только внутри тела функции noexcept, но и на сайтах, где вызывается функция. Такая гибкость присутствует только в случае отсутствия функций. Функции с спецификациями исключений "throw()" этого не имеют, как и функции без спецификации исключения.

Ответ 3

Я разместил 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 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.

Inheriting constructors, а implicitly-declared default constructors, copy constructors, move constructors, destructors, copy-assignment operators, move-assignment operators - все noexcept(true) по умолчанию, если только они не требуются для вызова функции noexcept(false), и в этом случае эти функции noexcept(false).

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

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

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

Вы можете узнать об этом подробнее → http://scottmeyers.blogspot.dk/2014/03/declare-functions-noexcept-whenever.html