[dcl.attr.noreturn] は次の例を提供します。
[[ noreturn ]] void f() {
throw "error";
// OK
}
しかし、私は[[noreturn]]
。関数の戻り値の型はすでにvoid
であるためです。
それで、noreturn
属性のポイントは何ですか?どのように使用することになっていますか?
Noreturn属性は、呼び出し元に戻らない関数に使用されることになっています。これはvoid関数(呼び出し側に戻る-値を返さない)を意味するものではありませんが、関数の終了後に制御フローが呼び出し側の関数に戻らない関数(アプリケーションを終了する関数、永久にループするか、例のように例外をスローします)。
コンパイラーはこれを使用して、最適化を行い、より良い警告を生成できます。たとえば、f
にnoreturn属性がある場合、g()
を記述するときに、コンパイラはf(); g();
がデッドコードであることを警告できます。同様に、コンパイラーは、f()
の呼び出し後にreturnステートメントがないことを警告しないことを知っています。
noreturn
は、関数が値を返さないことをコンパイラーに通知しません。 制御フローが呼び出し元に戻らないであることをコンパイラーに伝えます。これにより、コンパイラはさまざまな最適化を行うことができます-呼び出しの周りの揮発性状態を保存および復元する必要がなく、そうでなければ呼び出しに従うコードをデッドコードで排除することができます.
これは、機能が完了しないことを意味します。制御フローは、f()
の呼び出し後にステートメントにヒットすることはありません。
_void g() {
f();
// unreachable:
std::cout << "No! That's impossible" << std::endl;
}
_
この情報は、コンパイラ/オプティマイザがさまざまな方法で使用できます。コンパイラは、上記のコードに到達できないという警告を追加できます。また、継続をサポートするなど、さまざまな方法でg()
の実際のコードを変更できます。
以前の回答では、noreturnが何であるかは正しく説明されていましたが、whyは存在しません。 「最適化」コメントが主な目的だとは思いません。返されない関数はまれであり、通常は最適化する必要はありません。むしろ、私が帰らないことの主な理由は、偽陽性の警告を避けることだと思います。たとえば、次のコードを検討してください。
int f(bool b){
if (b) {
return 7;
} else {
abort();
}
}
Abort()が「noreturn」とマークされていなかった場合、コンパイラは、fが期待どおりに整数を返さないパスを持つこのコードについて警告した可能性があります。ただし、abort()はリターンなしとマークされているため、コードが正しいことがわかります。
理論的に言えば、void
は他の言語unit
またはtop
で呼ばれるものです。論理的に同等なものはTrueです。任意の値を正当にvoid
にキャストできます(すべてのタイプはvoid
のサブタイプです)。 「宇宙」セットと考えてください。 allの値に共通する操作はないため、タイプ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()の値が空のセットに属している」と言ったら、それはhighly情報的である-この式は通常の実行を完了することはありません。中止、スロー、またはハングします。 void
の正反対です。
したがって、以下は意味がありません(noreturn
はファーストクラスのC++型ではないため、擬似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;
}
_
完璧な世界では、上記の関数raise()
の戻り値としてnoreturn
を使用できます。
_noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();
_
残念なことに、おそらく実用的な理由から、C++では許可されていません。代わりに、コンパイラ最適化と警告のガイドに役立つ_[[ noreturn ]]
_属性を使用する機能を提供します。