40

私はB. Stroustrup によるC++17 についての考え でコントラクトについて読み、それらについて話す小さなプレゼンテーションを支援しましたが、それらを本当に理解しているかどうかはわかりません。

だから私はいくつかの尋問があり、いくつかの例でそれらを説明することが可能であれば:

  • コントラクトはクラシックのより良い代替品であり、assert()一緒に使用する必要がありますか? ソフトウェア開発者にとって、実際に簡単な言葉で表される契約は何ですか?

  • 契約は、例外の処理方法に影響を与えますか? はいの場合、例外と契約をどのように使用する必要がありますか?

  • コントラクトを使用すると、実行時のオーバーヘッドが発生しますか? リリースコードでそれらを無効にすることはできますか?

提案 N4415から:

Vector クラスのインデックス演算子の事前条件コントラクトは、次のように記述できます。
T& operator[](size_t i) [[expects: i < size()]];

同様に、ArrayView クラスのコンストラクターの事後条件コントラクトは、次のように表現できます。 ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];

@Keith Thompson のコメントに感謝します。

コントラクトは C++20 にはなりませんでした新しい研究会SG21が発足しました。

4

3 に答える 3

24

私がこのドキュメントから読んだ限り: 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; // Just for the sake of the example...
            data = new Data[capacity*2]; // Might throw an exception
            // Copy p into data and delete p
        }
        // Add the element to the end
    }
private:
     Data* data;
     // other data
};

この例では、neworDataのコンストラクターが例外をスローした場合、事後条件に違反しています。これは、契約に違反しないように、そのようなコードをすべて変更する必要があることを意味します!

もちろん、 と同様assertに、コントラクトには実行時のオーバーヘッドが含まれる場合があります。ただし、違いは、コントラクトを関数の宣言の一部として配置できるため、コンパイラは、呼び出し元のサイトで条件を評価したり、コンパイル時に条件を評価したりするなど、より適切な最適化を実行できることです。この投稿の冒頭で言及したドキュメントのセクション 1.5 では、ビルド構成に応じてコントラクトをオフにする可能性について説明しています。

于 2015-07-09T12:30:16.503 に答える
10

提供された元のドキュメント OP からのリンクから始めました。いくつかの答えがあると思います。その紙から始めることを強くお勧めします。TL&DR バージョンは次のとおりです。

コントラクトは、一般的なエラー報告メカニズムではなく、テスト フレームワークの代わりにもなりません。むしろ、プログラムの部分間の期待の不一致が原因でプログラムがうまくいかない場合の基本的な緩和策を提供します。コントラクトは、概念的には言語に統合された構造化された assert() に似ており、言語のセマンティクス ルールに従って動作します。したがって、原則的なプログラム分析とツールの基礎となります。

ご質問について:

  • 構造化された assert() なので、場合によっては assert() をコントラクトに置き換える必要があると言えます。
  • ここで別の引用を使用しましょう。

...コントラクトの式は、論理的に操作の宣言の一部である必要があります。

例:

T& operator[](size_t i) [[expects: i < size()]];

私の意見では、これは素晴らしく読みやすいものです。

  • 場合によっては、契約は例外を置き換えることができます:

ただし、コントラクトが組み込みシステムまたは例外を許容できないその他のリソースに制約のあるシステムで使用できることは、重要な設計基準です。

前提条件コントラクトの失敗後のさらなる動作は保証されないため、前提条件コントラクトでは例外を引き続き使用できます。

  • オーバーヘッドは、すべて使用、非使用、事前条件のみ、事後条件のみのケースでコントラクト チェックのオン/オフを切り替えることで削減できます。コントラクトをオンにすると、あらゆる種類のチェックと同様に、オーバーヘッドが確実に追加されます。

いくつかの使用例 (コントラクト設計の開発に近づいていない間に推測できるように)

  1. assert()コントラクト -コントラクトはより読みやすく、コンパイル時に最適化できるため、通常の場合。
  2. アサート - 単体テスト、テスト フレームワークなど。
  3. 例外 - 記事に記載されているように、事前条件付きのコントラクトで使用できます。

操作の前提条件は、関数本体の他のステートメントよりも前に評価されます。結果が true の場合、実行の通常の制御は、関数本体の最初のステートメントに続きます。それ以外の場合、それ以降の実行は保証されません。プログラムが中止されるか、例外がスローされるか、続行が許可されている場合の動作は未定義です。

コントラクトの実装については他にもいくつか の提案があるため、調査は時期尚早です。

于 2015-07-09T12:35:08.353 に答える