В чем смысл noreturn?


[dcl.АТР.noreturn] приводится следующий пример:

[[ noreturn ]] void f() {
    throw "error";
    // OK
}

но я не понимаю, в чем смысл [[noreturn]], потому что возвращаемый тип функции-это уже void.

Итак, в чем же смысл ? Как он должен использоваться?

5 144

5 ответов:

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

это может быть использовано компиляторами, чтобы сделать некоторые оптимизации и генерировать лучше предупреждение. Например, если f с атрибутом noreturn, компилятор может предупредить вас о g() будучи мертвым кодом, когда вы пишете f(); g();. Точно так же компилятор будет знать, чтобы не предупреждать вас о пропущенных операторах возврата после вызовов f().

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

Это означает, что функция не будет выполнена. Поток управления никогда не попадет в Инструкцию после вызова f():

void g() {
   f();
   // unreachable:
   std::cout << "No! That's impossible" << std::endl;
}

информация может быть использована компилятором / оптимизатором по-разному. Компилятор может добавить предупреждение о том, что приведенный выше код недоступен, и он может изменить фактический код g() по-разному, например, для поддержки продолжения.

тип теоретически говоря, void это то, что называется в других языках unit или top. Его логический эквивалент правда. Любое значение может быть законно приведено к void (каждый тип является подтипом void). Подумайте об этом как о наборе "Вселенная"; нет никаких операций, общих для все значения в мире, поэтому нет допустимых операций над значением типа void. Иначе говоря, говоря вам, что что-то принадлежит к набору Вселенной дает вам никакой информации - вы уже знаете это. Итак, звучит следующее:

(void)5;
(void)foo(); // whatever foo() does

но задание не ниже:

void raise();
void f(int y) {
    int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
    cout << x << endl;
}

[[noreturn]], С другой стороны, иногда называют empty,Nothing,Bottom или Bot и является логическим эквивалентом ложные. Он вообще не имеет значений, и выражение этого типа может быть приведено к (т. е. является подтипом) любого типа. Это пустой набор. Обратите внимание, что если кто-то говорит вам "ценность выражение foo () принадлежит пустому набору" it is очень информативный - он говорит вам, что это выражение никогда не завершит свое нормальное выполнение; он будет прерван, бросить или повесить. Это полная противоположность void.

поэтому следующее не имеет смысла (псевдо-C++, так как noreturn не является первоклассным типом C++)

void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns

но задание ниже совершенно законно, так как throw понимается компилятором не так возвращение:

void f(int y) {
    int x = y!=0 ? 100/y : throw exception();
    cout << x << endl;
}

в идеальном мире, вы могли бы использовать noreturn как возвращаемое значение для функции raise() выше:

noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();

к сожалению, C++ не позволяет этого, вероятно, по практическим причинам. Вместо этого он дает вам возможность использовать [[ noreturn ]] атрибут, который помогает направлять оптимизацию компилятора и предупреждения.

предыдущие ответы правильно объяснили, что такое noreturn, но не почему он существует. Я не думаю, что комментарии "оптимизация" - это основная цель: функции, которые не возвращаются, редки и обычно не нуждаются в оптимизации. Скорее, я думаю, что главный смысл существования noreturn заключается в том, чтобы избегать ложноположительных предупреждений. Например, рассмотрим следующий код:

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }

если бы abort() не был отмечен "noreturn", компилятор мог бы предупредить об этом коде, имеющем путь где F не возвращает целое число, как ожидалось. Но поскольку abort () помечен как No return, он знает, что код правильный.