概要
※ここでは各用語の日本語訳については、できるだけ JIS C++ (JIS X 3014:2003) に倣った。例外についてはそれぞれ注釈で説明を与える。
処理系の適合 (implementation compliance)
処理系 (implementation) または実装とはプログラムの翻訳 (translation) と実行 (execution) をする一連の枠組み・ソフトウェアのことである。 C++ の処理系は、翻訳を担うコンパイラと実行を担うオペレーティングシステムから構成されるのが普通だが、後者は前提として特に前者を処理系と考えることも多い。 標準規格 (standard) または規格とは、C++ の適合する処理系 (conforming implementation) が満たすべき要件 (requirement) を取り決めた文書である。
動作
適合する処理系は標準規格が定める抽象機械 (abstract machine) の外から見た動作 (observable behavior) を模倣 (emulate) しなければならない。 外から見た動作は
volatile
glvalue を通した抽象機械の読み書き操作- 実行終了後の、ファイルに出力された内容
- 対話的な入出力装置の読み書きの順序
からなる。但し、一部の動作については処理系に対して自由度が認められている。
- 処理系定義の動作 (implementation-defined behavior) または実装定義の動作とされた動作に対しては、処理系は考えられる動作の内の1つを行って良い。処理系はその説明書にその動作を定義する必要がある。
- 未規定の動作 (unspecified behavior) とされた動作に対しては、処理系は考えられる動作の内の1つを行って良い。処理系は説明書にその動作を定義しなくて良い。
- 未定義の動作 (undefined behavior; 通称 UB) は、処理系が実際に行う動作について標準規格が如何なる要件もおかないことを表す。
- 文化圏固有動作 (locale-specific behavior) に対しては、処理系は現地の国家・文化・言語の風習に依存した動作を行う。処理系はその動作を説明書に記述する必要がある。
- エラー性の動作 (erroneous behavior; 通称 EB) は、定義された動作であるが、処理系は診断情報を出力することが推奨される。その後に未規定の時点で、処理系は実行を終了しても良い。
これらの用語は処理系が取りうる動作の範囲を示すものであって、例えば "未定義の動作" という名前の具体的な動作がある訳ではないことに注意する。
規則
標準規格の定める要件は、処理系 (抽象機械) に対する直接の要件と、処理系が受け入れるべきプログラムが満たす規則 (rule) で構成される。 規則には幾つかの分類があるが、C++ の標準規格内では具体的な分類基準は示されていない。C言語に倣えば以下の解釈になる。
- 構文規則 (syntactic rule, syntax rule): 拡張バッカス・ナウア記法の亜種である構文記法 (syntax notation) によって指定されるプログラムの構文
- 制約 (constraints): C言語において構文記法によって表現しきれない構文的な制限を文章で述べたもの。C++ には現れない
- 意味規則 (semantic rule): 構文規則と制約のどちらでもないプログラムに対する規則
各規則には診断不要[注1] (no diagnostic required; 通称 NDR) や「違反すると未定義の動作になる」などの属性が明記されることがあり、 診断不要とも未定義の動作になるとも明記されない規則を診断対象規則 (diagnosable rule) と呼ぶ。 単一定義規則 (ODR; one definition rule) は "使用される変数・関数・クラスについてただ1つの定義を与えなければならない" という一連の規則である。
- 適格 (well-formed) とはプログラムが全ての構文規則・診断対象の意味規則・単一定義規則を満たすことである
- 不適格 (ill-formed) とはプログラムが適格でないことである
プログラムが規則に違反するとき、処理系はエラーメッセージまたは警告などを出力する。 この出力を総称して診断情報 (diagnostic message) または診断メッセージ[注2] と呼び、その内容は処理系定義である。
適合する処理系は、
- 規則を全て満たすプログラムをそのリソースの範囲で正しく実行 (外から見た動作を模倣) する必要がある。
- 診断対象規則に違反するプログラムに対して診断情報を出力する必要がある。
- 診断不要な規則に違反するプログラムの翻訳・実行について、標準規格によって如何なる要件もおかれない。
条件付き対応の構成
一部の C++ の機能は条件付き対応の構成[注3] (conditionally-supported constructs) とされ、処理系はこれに対応しなくても良い。 対応する場合にはその動作は処理系定義である。
処理系限界
変数名の最大の長さや仮引数の最大の数など、処理系が対応する様々なプログラムの大きさのことを処理系限界 (implementation limit, implementation quantity) と呼ぶ。 処理系は処理系限界についての情報を説明書に記述する必要がある。
規則違反と動作の包含関係
規則に違反するプログラム・処理系依存の動作を行うプログラムの包含関係について以下の図に示す。
- 但し、一つのプログラムが複数の規則に違反する場合については考慮していない。
- 構文規則の範囲は規格上曖昧なので、診断不要 (NDR) の規則を含むかどうかは解釈による。
- 不適格かつ診断不要の規則 (ill-formed; NDR) が、規格上矛盾なく定義されているかどうかは議論が分かれる。
慣用語
処理系の性質についての慣用語
- 合法 (legal): 処理系が適合することを指す。
- 違法 (illegal): 処理系が適合しないことを指す。
- QoI (quality of implementation; QOI): 処理系の実装品質のこと。適合する処理系であっても処理系依存な部分で粗末な動作をするものが考えうる。
- normative: (ある規格の記述が) 適合する処理系に対して強制力を持つさまを表す
- informative: (ある規格の記述が) 強制力を持たないさまを表す
プログラムの性質についての慣用語
- 処理系依存 (implementation-dependent): プログラムの動作が処理系によって異なりうること。 条件付き対応の機能を使用している場合や、処理系定義の動作・未規定の動作・未定義の動作・文化圏固有動作を起こす場合、処理系限界を超える場合、規則に違反している場合を含む。
- 合法 (legal)・違法 (illegal): これらの語はプログラムに対しても慣用されるが、具体的な意味は明確でない。 プログラムの正しさには複数の水準があるためである。 適格、またはすべての規則を満たす、または未定義の動作を含まないなどが考えられる。 曖昧さを避けるため、このサイトではプログラムに対して合法・違法という語は用いない。
「鼻から悪魔」とプログラムの可搬性
プログラムが
とき、標準規格は適合する処理系に対して何らの要件も課さない。 つまり、UB または NDR 違反を含むプログラムに対して処理系がいかなる動作をしても規格には抵触しないということを表す。 例えば、このことで処理系が鼻から悪魔を出しても、それはプログラムの作者の責任であり、その処理系を責めることはできない。 この冗談を鼻から悪魔 (nasal demons)[1][2] と呼ぶ。鼻から悪魔を出す処理系は今のところ実在しないが、 実際の未定義の動作として最適化の過程で或る種の「タイムトラベル」を起こす処理系は実在する[3]。
処理系依存のプログラムは、たとえ或る処理系の上で期待する動作をしたとしても、他の処理系でも正しく動作することは保証されない。 可搬なプログラムを書くためには、未定義の動作を引き起こさずかつ診断不要の規則に違反しないプログラムを書くように心懸ける必要がある。 更に、処理系定義の動作や未規定の動作は、(内部的に起こしても良いが) 外から見える動作として現れて問題を起こさないようにする必要がある。 余裕があれば、文化圏固有動作や条件付き対応の構成についても気を配ると良い。
文化圏固有動作の一覧
ToDo
条件付き対応の一覧
ToDo
処理系限界の一覧
ToDo
参照
- C++er は“合法”だとか“違法”だとか言いたくて仕方がないけれど、結局どういう意味? それより適合・適格・○○動作・○○規則・診断不要いろいろの関係が謎 - Qiita
- 処理系定義の動作 - C言語の処理系定義の動作の一覧
- 未定義の動作 - C言語の未定義の動作の一覧
- 未規定の動作 - C言語の未規定の動作の一覧
- 文化圏固有動作 - C言語の文化圏固有動作の一覧
- 本の虫: Old New Thing: 未定義動作はタイムトラベルを引き起こす(他にもいろいろあるけど、タイムトラベルが一番ぶっ飛んでる)
- MSC15-C. 未定義の動作に依存しない
出典
- ^ nasal demons
- ^ 本の虫: C++0x本:鼻から悪魔
- ^ 本の虫: Old New Thing: 未定義動作はタイムトラベルを引き起こす(他にもいろいろあるけど、タイムトラベルが一番ぶっ飛んでる)
注釈
- ^ no diagnostic required: JIS C++ では一定の訳は与えられず、登場する度に異なる翻訳のされ方をしている。 ここでは "診断不要" という語を割り当てることにする。
- ^ diagnostic message: JIS C++ では "診断情報" としている。 JIS C言語 (JIS X 3010:2003) では "診断メッセージ" としている。
- ^ conditionally-supported constructs: C++11 で導入されたものなので、JIS C++ には対応訳は存在しない。 ここでは "条件付き対応の構成" と訳す。 因みに JIS C++ では constructs は "構文" と訳している。 現に C++98/03 で constructs という単語が使われているのは構文に対してのみである。 しかし conditionally-supported constructs が導入された今、 constructs という単語は構文とは言い難いものにも使われている。 因みに、本来 constructs というのは構成要素というぐらいの意味である。