web-dev-qa-db-ja.com

ノーリターンのポイントは何ですか?

[dcl.attr.noreturn] は次の例を提供します。

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

しかし、私は[[noreturn]]。関数の戻り値の型はすでにvoidであるためです。

それで、noreturn属性のポイントは何ですか?どのように使用することになっていますか?

168
BЈовић

Noreturn属性は、呼び出し元に戻らない関数に使用されることになっています。これはvoid関数(呼び出し側に戻る-値を返さない)を意味するものではありませんが、関数の終了後に制御フローが呼び出し側の関数に戻らない関数(アプリケーションを終了する関数、永久にループするか、例のように例外をスローします)。

コンパイラーはこれを使用して、最適化を行い、より良い警告を生成できます。たとえば、fにnoreturn属性がある場合、g()を記述するときに、コンパイラはf(); g();がデッドコードであることを警告できます。同様に、コンパイラーは、f()の呼び出し後にreturnステートメントがないことを警告しないことを知っています。

188
sepp2k

noreturnは、関数が値を返さないことをコンパイラーに通知しません。 制御フローが呼び出し元に戻らないであることをコンパイラーに伝えます。これにより、コンパイラはさまざまな最適化を行うことができます-呼び出しの周りの揮発性状態を保存および復元する必要がなく、そうでなければ呼び出しに従うコードをデッドコードで排除することができます.

57
Stephen Canon

これは、機能が完了しないことを意味します。制御フローは、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()はリターンなしとマークされているため、コードが正しいことがわかります。

14
Nadav Har'El

理論的に言えば、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]]_は、時々emptyNothingBottom、または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 ]]_属性を使用する機能を提供します。

11
Elazar