19

私が知る限り、SO コミュニティは、関数を宣言することで、他のnoexcept方法では不可能な意味のあるコンパイラーの最適化が可能になるかどうかについて意見が分かれています。(特にコンパイラの最適化について話しているのであって、に基づくライブラリ実装の最適化ではありませんmove_if_noexcept。) この質問の目的のために、noexcept意味のあるコード生成の最適化が可能になると仮定しましょう。inlineその前提で、関数を宣言することは理にかなっていますnoexceptか? そのような関数が実際にインライン化されていると仮定すると、呼び出しサイトで関数tryから生じるコードの周りにブロックに相当するものをコンパイラが生成する必要があるように思われます。それなしinlineterminatenoexcept、そのtryブロックは不要のようです。

noexcept私の当初の関心は、Lambda 関数が暗黙的に であることを考えると、宣言する意味があるかどうかということでしたが、Lambdaだけでなく、どの関数でinlineも同じ問題が発生することに気付きました。inline

4

2 に答える 2

11

noexcept が有意義なコード生成の最適化を可能にすると仮定しましょう

わかった

そのような関数が実際にインライン化されていると仮定すると、呼び出しサイトでインライン関数から生じるコードの周りに try ブロックに相当するものをコンパイラが生成する必要があるように思われます。その領域で例外が発生した場合

コンパイラが関数本体を見て、何もスローできない可能性があることを確認できる可能性があるため、必ずしもそうではありません。したがって、名目上の例外処理は省略できます。

関数が「完全に」インライン化されている場合 (つまり、インライン化されたコードに関数呼び出しが含まれていない場合) は、コンパイラがこの決定をかなり一般的に行うことができると予想されますが、たとえば、vector::push_back()およびへの呼び出しがある場合はそうではありません関数の作成者は、十分なスペースが予約されていることを認識していますが、コンパイラは認識していません。

また、適切な実装では、何もスローされない場合に try ブロックを実行するためにコードをまったく必要としない可能性があることにも注意してください。

その前提で、インライン関数 noexcept を宣言することは理にかなっていますか?

はい、仮定された最適化が何であるかを取得するためにnoexcept

于 2014-01-30T17:34:39.563 に答える
3

nothrow 関連の問題について、権力の輪で興味深い議論があったことは注目に値します。これらを読むことを強くお勧めします:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3227.html

http://www.stroustrup.com/N3202-noexcept.pdf

どうやら、非常に影響力のある人々が、C++ になんらかの自動 nothrow 演繹を追加することに関心を持っているようです。

少し熟考した後、私は自分の立場をほぼ反対に変更しました。以下を参照してください

このことを考慮:

  • noexcepton宣言を持つ関数を呼び出すと、このメリットが得られます (巻き戻しなどを処理する必要はありません)。

  • コンパイラが定義noexceptされている関数をコンパイルすると、(関数が実際にスローされないことをコンパイラが証明できない限り)パフォーマンスが低下します(コンパイラは、例外がこの関数をエスケープできないことを確認する必要があります)。例外なしの約束を強制するように求めています

つまりnoexcept、あなたを傷つけ、あなたに利益をもたらします。関数がインライン化されている場合はそうではありません! インライン化されている場合-宣言の noexcept からの利点はまったくありません(宣言と定義が1つになります)...それは、安全のためにコンパイラにこれを強制させたい場合を除きます。安全は、間違った結果を生成するよりも終了したいということです。

今では明らかなはずです-インライン関数を宣言する意味はありませnoexceptん(すべてのインライン関数がインライン化されるわけではないことに注意してください)。

スローしない関数のさまざまなカテゴリを見てみましょう (スローしないことはわかっています)。

  1. インライン化されていない場合、コンパイラはそれがスローされないことを証明できます --noexcept関数本体を傷つけることはなく (コンパイラは仕様を単に無視します)、呼び出しサイトはこの約束の恩恵を受けます

  2. インライン化されていないため、コンパイラはそれがスローされないことを証明できません --noexcept関数本体を傷つけますが、呼び出しサイトに利益をもたらします (何がより有益かを判断するのは困難です)

  3. インライン化され、コンパイラはそれがスローされないことを証明できます --noexcept目的を果たしません

  4. インライン、コンパイラはそれがスローされないことを証明できません --noexcept呼び出しサイトを傷つけます

ご覧のとおり、nothrowは単純に設計が不適切な言語機能です。例外なしの約束を強制したい場合にのみ機能します。正しく使用する方法はありません。「安全性」は得られますが、パフォーマンスは得られません。

noexceptキーワードは、約束(宣言時)と強制(定義時)の両方として使用されることになりました-完璧なアプローチではないと思います(笑、例外仕様で2回目の刺し傷を負いましたが、まだ正しくありませんでした)。

じゃあ何をすればいいの?

  1. あなたの行動を宣言してください(悲しいかな、言語はここであなたを助けるものは何もありません)!例えば:

    void push_back(int k);   // throws only if there is no unused memory
    
  2. noexceptインライン関数を使用しないでください (非常に大きいなど、インライン化される可能性が低い場合を除きます)

  3. 非インライン関数 (またはインライン化される可能性が低い関数) の場合 -- 呼び出しを行います。より大きな関数は、より小さなnoexceptの負の効果を(比較的)得ます-ある時点で、呼び出し元の利益のためにそれを指定することはおそらく理にかなっています

  4. noexceptmove コンストラクターと move 代入演算子 (およびデストラクタ?) で使用します。それらに悪影響を与える可能性がありますが、そうしないと、特定のライブラリ関数 (std::swap、一部のコンテナー操作) が最も効率的なパスを取りません (または、最良の例外保証を提供しません)。基本的に、(現時点では) 関数でnoexcept operatorを使用する場所では、 noexcept specifierを使用する必要があります。

  5. noexcept関数が行う呼び出しを信頼せず、予期しない動作をさせるよりも死ぬ場合に使用します

  6. 純粋な仮想関数 -- 多くの場合、これらのインターフェイスを実装している人を信頼していません。多くの場合、保険を購入することは理にかなっています (指定することによりnoexcept)

さて、他にどのようnoexceptに設計できますか?

  1. 2 つの異なるキーワードを使用します。1 つは約束を宣言するためのもので、もう 1 つはそれを実施するためのものです。たとえば、 noexceptforce_noexcept。実際、強制は実際には必要ありません。それは try/catch + terminate() で実行できます (はい、スタックをアンワインドしますが、その後にstd::terminate()が続くかどうかは誰が気にしますか?)

  2. コンパイラーに、特定の関数内のすべての呼び出しを分析させて、スローできるかどうかを判断させます。そうであり、noexcept の約束がなされた場合 -- コンパイラ エラーが発生します。

  3. スローできるが、スローできないことがわかっているコードの場合、コンパイラーが問題ないことを保証する方法が必要です。次のようにします。

    vector<int> v;
    v.reserve(1);
    ...
    nothrow {       // memory pre-allocated, this won't throw
        v.push_back(10);
    }
    

    promise が破られた場合 (つまり、誰かがベクトル コードを変更し、他の保証を提供するようになった場合) -- 未定義の動作。

免責事項: このアプローチは非現実的すぎる可能性があります。

于 2016-08-31T06:23:45.703 に答える