私はB. Stroustrupによる Thoughts about C++ 17 で契約について読んでいて、それらについて話している小さなプレゼンテーションを支援しましたが、本当に理解したかどうかはわかりません。
だから私はいくつかの尋問があり、いくつかの例でそれらを説明することが可能であれば:
コントラクトは従来のassert()
をより良く置き換えたものであり、一緒に使用すべきですか?ソフトウェア開発者にとって、実際にはどのような契約が単純な言葉で書かれていますか?
契約は例外の処理方法に影響を与えますか?はいの場合、例外と契約をどのように使用すればよいですか?
契約を使用すると、実行時にオーバーヘッドが発生しますか?リリースコードでそれらを無効にすることはできますか?
提案N4415 から:
Vectorクラスのインデックス演算子の前提条件コントラクトを作成できます。
T& operator[](size_t i) [[expects: i < size()]];
同様に、ArrayViewクラスのコンストラクターの事後条件コントラクトは、次のように表現できます:
ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];
@キース・トンプソンのコメントに感謝:
ContractsはC++ 20に入れませんでした。 新しい研究グループSG21が作成されました。
このドキュメントから読んだ限り: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf
契約は、assert
が長年にわたって原始的な方法でやろうとしていたことを行います。それらは、ドキュメントと、呼び出し元が関数を呼び出す方法と、関数が返された後の呼び出し元がコードを期待できる状態の実行時アサートの両方です。これらは通常、事前条件と事後条件、または不変条件として知られています。
これにより、実装側のコードをクリーンアップできます。コントラクトでは、関数内で実行が完了すると、引数は有効な状態(想定どおり)になると想定できるためです。
契約では例外をスローしても事後条件が破壊されないようにする必要があるため、事後条件の部分は例外の処理方法を変更する可能性があります。通常、これはコードが例外に対して安全でなければならないことを意味しますが、それが強力な例外保証を意味するか、基本的な保証を意味するかは条件によって異なります。
例:
class Data; class MyVector { public: void MyVector :: Push_back(Elem e)[[ensures:data!= nullptr]] { if(size> = capacity) { Data * p = data; data = nullptr; //例のためだけに... data = new Data [capacity * 2]; //例外をスローする可能性があります // pをデータにコピーし、p を削除します} //末尾に要素を追加します } private: Data * data; //その他のデータ };
この例では、new
またはData
のコンストラクターが例外をスローした場合、事後条件に違反します。つまり、このようなコードをすべて変更して、契約に違反しないようにする必要があります。
もちろん、assert
と同様に、契約には実行時のオーバーヘッドが含まれる場合があります。ただし違いは、コントラクトを関数の宣言の一部として配置できるため、呼び出し側のサイトで条件を評価したり、コンパイル時に評価したりするなど、コンパイラーがより最適化できることです。この投稿の冒頭で言及したドキュメントのセクション1.5では、ビルド構成に応じてコントラクトをオフにする可能性について説明しています。
提供された元のドキュメントOPの link から始めました。いくつかの答えがあると思います。その論文から始めることを強くお勧めします。 TL&DRバージョンは次のとおりです。
コントラクトは一般的なエラー報告メカニズムではなく、テストフレームワークの代わりにもなりません。むしろ、プログラムの部分間の期待の不一致が原因でプログラムが失敗した場合の基本的な緩和策を提供します。契約は、概念的には言語に統合された構造化されたassert()に似ており、言語セマンティクスルールに従って動作します。したがって、原則的なプログラム分析とツールの基礎となります。
ご質問について:
...契約の式は論理的に操作の宣言の一部である必要があります。
および例:
_T& operator[](size_t i) [[expects: i < size()]];
_
私の意見では、これは見事で読みやすいものです。
ただし、組み込みシステムや、例外を許容できない他のリソースに制約のあるシステムでコントラクトを使用できるようにすることは、重要な設計基準です。
前提条件の契約の失敗後の動作が保証されないため、前提条件の契約では例外を引き続き使用できます。
いくつかのユースケース(コントラクト設計の開発に近づいていないと思うが)
assert()
の場合。操作の前提条件は、関数の本体の他のステートメントの前に評価されます。結果がtrueの場合、実行の通常の制御は、関数の本文の最初のステートメントまで続きます。それ以外の場合、それ以上の実行は保証されません。プログラムが異常終了するか、例外をスローするか、続行が許可されている場合、動作は未定義です。
それ以外の質問に答えることは簡単ではありません。これは、どの契約が正確に行われるかがまだ明確でないためです。現在、いくつかの提案やアイデアが浮かんでいます。
n4378 Lakos et al。 は、基本的に洗練されたアサートツールキットを標準化することを提案しています。コントラクトは関数実装内でチェックされ、ランタイムチェックの量を制御するために3つの異なるアサートレベルが提供され、アサート違反の処理をカスタマイズできます。
n4415 dos Reis et al。 と n4435 Brown はかなり似ており、関数インターフェイスで事前条件と事後条件を定義する属性ベースの構文を提案しています。実行時チェックと違反時の動作をどの程度制御できるかについては詳しく説明しません。
このトピックに関する最近の論文もあまりありません。まだ決定されていない多くの詳細があり、この機能は多くの異なる領域(モジュール、最適化、構築/リンクなど)に影響を与えますが、その一部は標準ではほとんど制御できません。
契約違反の処理と例外の相互作用が不明確であるため、例外に関する質問は特に困難です(例:契約違反ハンドラーがスローする可能性があります(テストフレームワークで有用)。関数がnoexcept(true)
?である場合)。