回答:
IB:実装定義の動作。標準では、正確な動作を定義するために特定のコンパイラ/プラットフォームに任せていますが、定義する必要があります。
実装定義の動作を使用すると便利ですが、コードの移植性が低下します。
UB:未定義の動作。標準では、未定義の動作を呼び出すプログラムの動作を指定していません。理論的には悪魔が鼻から飛び出す可能性があるため、「鼻の悪魔」とも呼ばれます。
未定義の動作を使用することは、ほとんど常に悪い考えです。動作するように見える場合でも、環境、コンパイラ、またはプラットフォームに変更を加えると、コードがランダムに破損する可能性があります。
実装定義の動作と未定義の動作
C ++標準は、さまざまな構成の影響について非常に具体的であり、特に、以下のカテゴリの問題に常に注意する必要があります。
未定義の動作とは、保証がまったくないことを意味します。コードが機能するか、ハードドライブに火をつけたり、悪魔に鼻を飛ばしたりできます。C ++言語に関する限り、絶対に何かが起こる可能性があります。実際には、これは通常、回復不可能なバグがあることを意味します。これが発生した場合、アプリケーションについて何も信頼することはできません(この未定義の動作の影響の1つは、アプリの他の部分で使用されているメモリを台無しにするためであったためです)。一貫性を保つ必要はないため、プログラムを2回実行すると結果が異なる場合があります。それは月の満ち欠け、着ているシャツの色、その他すべてに依存します。
未指定の動作とは、プログラムが正常で一貫した処理を行う必要があることを意味しますが、これを文書化する必要はありません。
実装定義の動作は未指定に似ていますが、コンパイラの作成者も文書化する必要があります。この例は、の結果ですreinterpret_cast
。通常、これはアドレスを変更せずにポインタのタイプを変更するだけですが、マッピングは実際には実装定義であるため、コンパイラがこの選択を文書化している限り、コンパイラは完全に異なるアドレスにマップできます。別の例は、intのサイズです。C ++標準では、2、4、8バイトのいずれでも問題ありませんが、コンパイラーによって文書化されている必要があります。
しかし、これらすべてに共通するのは、回避するのが最もよいということです。可能な場合は、C ++標準自体で100%指定されている動作を使用してください。これにより、移植性が保証されます。
多くの場合、実装によって定義された動作にも依存する必要があります。それは避けられないかもしれませんが、それでも注意を払い、異なるコンパイラ間で変更される可能性のあるものに依存していることに注意してください。
一方、未定義の動作は常に回避する必要があります。一般に、プログラムが何らかの方法で爆発することを前提とする必要があります。
IB:実装で定義された動作です-コンパイラーはその動作を文書化する必要があります。>>
負の値に対して演算を実行することは一例です。
UB:未定義の動作-コンパイラは、単にクラッシュしたり、予期しない結果を与えたりするなど、これまでに何でもできます。nullポインターの逆参照はこのカテゴリに分類されますが、配列オブジェクトの境界外にあるポインター算術などの微妙なことも含まれます。
別の関連用語は「不特定の動作」です。これは、実装で定義された動作と未定義の動作の間の一種です。不特定の動作の場合、コンパイラーは標準に従って何かを行う必要がありますが、標準が与える選択はコンパイラー次第であり、定義する必要はありません(または一貫性さえ必要ありません)。副次式の評価順序のようなものはこのカテゴリーに分類されます。コンパイラーは、これらを好きな順序で実行できます。また、ビルドによって、または同じビルドの実行ごとに異なる方法で実行できます(可能性は低いですが、許可されています)。
ショートバージョン:
実装定義の動作(IB):正しくプログラムされているが不確定*
未定義の動作(UB):正しくプログラムされていない(つまり、バグ!)
*)言語標準に関する限り、「不確定」です。もちろん、固定プラットフォームでは確定です。