В чем смысл нореткин?
[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 ]]
, который помогает направлять оптимизацию компилятора и предупреждения.