18

等値演算子には、ポインターに対する関係演算子の意味上の制限があります。

== (等しい) および != (等しくない) 演算子には、優先順位が低く、真偽値の結果を除いて、関係演算子と同じ意味制限、変換、および結果の型があります。[C++03 §5.10p2]

また、関係演算子には、ポインターの比較に関する制限があります。

同じ型の 2 つのポインター p と q が、同じオブジェクトのメンバーではない、同じ配列の要素ではない異なるオブジェクト、または異なる関数を指している場合、またはそれらの 1 つだけが null の場合、p<q, p の結果>q、p<=q、および p>=q は指定されていません。[§5.9p2]

これは等値演算子によって「継承」される意味制限ですか?

具体的には、次のとおりです。

int a[42];
int b[42];

(a + 3) < (b + 3) が指定されていないことは明らかですが、(a + 3) == (b + 3) も指定されていませんか?

4

3 に答える 3

15

とのセマンティクスは、マッピングがそれらの真理値 result を除いてop==いることを明示的に示しています。そのため、真理値の結果に対して定義されているものを調べる必要があります。結果が未規定であると彼らが言うなら、それは未規定です。彼らが特定のルールを定義している場合、そうではありません。特にそう言っているop!=

同じ型の 2 つのポインターは、両方が null であるか、両方が同じ関数を指しているか、または両方が同じアドレスを表している場合にのみ、等しいと見なされます。

于 2011-02-05T21:31:17.433 に答える
12

等値演算子 (==および!=) の結果は、ポインターが同じ型のオブジェクトを指している限り、指定された結果を生成します。同じ型への 2 つのポインターが与えられた場合、次のいずれかが当てはまります。

  1. 両方とも null ポインターであり、比較すると互いに等しくなります。
  2. 両方とも同じオブジェクトへのポインターであり、比較すると互いに同等です。
  3. それらは異なるオブジェクトへのポインターであり、互いに等しくないことを比較します。
  4. 少なくとも 1 つが初期化されておらず、比較の結果が定義されていません (実際には、比較自体は決して行われない可能性があります。比較を行うためにポインターを読み取ろうとすると、未定義の動作が発生します)。

同じ制約 (両方のポインターが同じ型のオブジェクトへのポインター) の下で、順序付け演算子 ( <<=>、 ) の結果は>=、両方が同じオブジェクトへのポインターであるか、同じ配列内の別のオブジェクトへのポインターである場合にのみ指定されます (およびこの目的のためにmallocnew、 などで割り当てられたメモリの「チャンク」は配列とみなされます)。ポインターが同じ配列の一部ではない別のオブジェクトを参照している場合、結果は規定されていません。一方または両方のポインターが初期化されていない場合、未定義の動作が発生します。

それにもかかわらず、標準ライブラリ ( std::lessstd::greaterstd::less_equalおよびstd::greater_equal)の比較テンプレートすべて、組み込みの演算子がそうでない場合でも意味のある結果を生成します。特に、それらは全順序を生成する必要があります。そのため、組み込みの比較演算子ではなく、必要に応じて順序付けを行うことができます (ただし、もちろん、ポインターのいずれかまたは両方が初期化されていない場合、動作は未定義のままです)。

于 2011-02-05T21:52:22.253 に答える
6

適合セマンティクスには混乱があるため、これらはC++のルールです。Cは完全に異なる適合モデルを使用しています。

  1. 未定義動作はオキシモロニックな用語です。つまり、プログラムではなく翻訳者が好きなように動作する可能性があります。これは一般的に、それが好きなことを何でもするコードを生成できることを意味します(しかしそれは控除です)。動作が未定義であると標準が述べている場合、このテキストを省略しても標準が翻訳者に課す要件は変更されないという意味で、テキストは実際にはユーザーにとって重要ではありません。

  2. 不正な形式のプログラムとは、特に指定がない限り、トランスレータの動作が厳密に定義されていることを意味します。プログラムを拒否し、診断メッセージを発行する必要があります。ここでの主な特殊なケースは、プログラムの形式が正しくないが診断が不要であることに違反した場合の単一定義規則です。

  3. 定義された実装は、動作を明示的に指定するドキュメントを含むという要件をトランスレータに課します。この特殊なケースでは、未定義動作が結果になる可能性がありますが、明示的に指定する必要があります。

  4. 不特定とは、振る舞いがセットから来ることを意味する愚かな用語です。この意味で、明確に定義されているのは、許可された動作のセットに1つの要素のみが含まれている特殊なケースです。Undefinedはドキュメントを必要としないため、ある意味では、ドキュメントなしで定義された実装と同じ意味です。

一般に、C ++標準は言語標準ではなく、言語標準のモデルです。実際の標準を生成するには、さまざまなパラメータをプラグインする必要があります。これらの中で最も認識しやすいのは、実装で定義された制限です。

標準にはいくつかのばかげた矛盾があります。たとえば、正当な翻訳者は、関数を提供する必要があるという理由で、明らかに優れたC ++プログラムをすべて拒否できますmain()が、翻訳者は1文字の識別子しかサポートしません。この問題は、QOIまたは実装品質の概念によって解決されます。それは基本的に、誰が気にするか、それが適合しているという理由だけで誰もそのコンパイラを購入するつもりはないと言っています。

技術的には、ポインタが無関係のオブジェクトを指す場合の不特定の性質はoperator <、おそらく次のことを意味することを意図しています。真または偽のいずれかの結果が得られますが、プログラムはクラッシュしませんが、これは不特定の正しい意味ではないため、は欠陥です。不特定の場合、許可された動作のセットを文書化するために標準の作成者に負担がかかります。これは、セットが開いている場合、未定義の動作と同等であるためです。

私は実際にstd::less、一部のデータ構造ではキーを完全に順序付ける必要があるという問題の解決策として提案しましたが、ポインターは完全に順序付けられていませんoperator <。線形アドレス指定を使用するほとんどのマシンlessでは、と同じです<less、たとえばx86プロセッサでの操作は潜在的に高価です。

于 2011-02-06T02:59:28.227 に答える