В чем смысл нореткин?

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

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

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

Итак, какова точка атрибута noreturn? Как он должен использоваться?

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

(void)5;
(void)foo(17); // whatever foo(17) 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 и является логическим эквивалентом False. У него вообще нет значений, и выражение этого типа может быть приведено к (то есть подтипу) любого типа. Это пустой набор. Обратите внимание, что если кто-то скажет вам, что "значение выражения foo() принадлежит пустому набору", это очень информативно - это говорит о том, что это выражение никогда не завершит свое нормальное выполнение; это прервет, бросит или повесит. Это полная противоположность 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 ]], который помогает направлять оптимизацию компилятора и предупреждения.