29

C ++のポインターは、一般に、同等性についてのみ比較できます。対照的に、未満の比較は、同じ完全なオブジェクトのサブオブジェクト(配列要素など)を指す2つのポインターに対してのみ許可されます。

したがってT * p, * q、を考えると、一般的に評価することは違法p < qです。

標準ライブラリにはstd::less<T>、組み込み演算子をラップするファンクタークラステンプレートなどが含まれてい<ます。ただし、標準では、ポインタの種類(20.8.5 / 8)について次のように述べています。

テンプレート、、、、greaterおよびの場合less、組み込み演算子、、、がそうでない場合でも、任意のポインター型の特殊化により全順序が生成されます。greater_equalless_equal<><=>=

これはどのように実現できますか?これを実装することさえ可能ですか?

GCC4.7.2とClang3.2を調べましたが、これらにはポインター型の特殊化はまったく含まれていません。<それらは、サポートされているすべてのプラットフォームで無条件に有効であることに依存しているようです。

4

5 に答える 5

26

ポインターを完全に順序付けることはできますか?ポータブルな標準C++ではありません。そのため、標準では、問題を解決するために、あなたではなく実装が必要です。ポインタの任意の表現について、任意の全順序を定義できるはずですが、それをどのように行うかは、ポインタの表現によって異なります。

フラットなアドレス空間とバイトアドレス指定を備えたマシンの場合、通常は、ポインタを同じサイズの整数または符号なし整数であるかのように扱うだけで十分です。これは、ほとんどのコンパイラがオブジェクト内の比較も処理する方法であるため、そのようなマシンでは、ライブラリが特殊化する必要はありませんstd::less。「不特定の」振る舞いはたまたま正しいことをします。

ワードアドレス指定されたマシンの場合(および少なくとも1つはまだ実稼働中です)、void* コンパイラーのネイティブ比較が機能する前に、ポインターをに変換する必要がある場合があります。

セグメント化されたアーキテクチャーを備えたマシンの場合、より多くの作業が必要になる場合があります。このようなマシンでは、アレイが完全に1つのセグメントに含まれている必要があり、セグメント内のオフセットを比較するのが一般的です。これは、 abが2つの任意のポインタである場合、で終わる可能性があるが、ではない可能性があること!(a < b) && !(b < a)を意味しますa == b。この場合、コンパイラーはstd::less<>、ポインターにet alの特殊化を提供する必要があります。これは、(おそらく)ポインターからセグメントとオフセットを抽出し、それらを何らかの操作で実行します。

編集:

言及する価値のある他のことについては、おそらく:C ++標準の保証は、標準C ++、またはこの場合は標準C++から取得されたポインターにのみ適用されます。最近のほとんどのシステムではmmap 、同じファイルを2つの異なるアドレス範囲に移動するのはかなり簡単で、2つのポインターがp ありq、比較は等しくありませんが、同じオブジェクトを指します。

于 2012-11-14T14:46:27.163 に答える
12

ポインタがグローバルな全順序を形成しないターゲットに標準ライブラリを実装することは可能ですか?

はい。有限集合が与えられると、いつでもその上に任意の全順序を定義できます。

可能なポインター値が5つしかない簡単な例を考えてみましょう。これらをO(nullptrの場合)、γ、ζ、χ、 ψ1と呼びましょう。

4つの非nullポインタからの2つの異なるポインタのペアをと比較できないとしましょう<。これにより、次の順序が得られると簡単に言うことができます。そうでないstd::less場合でも、Oζγψχ 。<

もちろん、この任意の順序を効率的に実装することは、実装の品質の問題です。


1私はギリシャ文字を使用して、ラテンアルファベットに精通しているために生じる可能性のある秩序の潜在意識の概念を取り除きます。ギリシャ語のアルファベット順を知っている読者に謝罪します

于 2012-11-14T13:58:54.487 に答える
5

フラットなアドレス空間を持つほとんどのプラットフォームでは、ポインター間の数値比較を簡単に行うことができます。これが不可能なプラットフォームでは、実装者はで使用する全順序を確立する他の方法を考え出す必要がありますが、保証が弱いstd::lessため、より効率的な方法を使用できる可能性があります。<

GCCとClangの場合、より強力な保証を提供する限り、std::less実装できます。彼らはの動作を実装しているので、この動作に依存することはできますが、将来変更される可能性があるため、ユーザーは信頼できません。<<<

于 2012-11-14T14:03:19.567 に答える
5

問題はセグメント化されたアーキテクチャであり、メモリアドレスにはセグメントとオフセットの2つの部分があります。これらの部分をある種の線形形式に変換するのは「十分に簡単」ですが、それには余分なコードが必要であり、そのオーバーヘッドをに課さないという決定がなされましたoperator<。セグメント化されたアーキテクチャの場合operator<、オフセットを単純に比較できます。この問題は、以前のバージョンのWindowsで発生していました。

「十分に簡単」はシステムプログラマーの視点であることに注意してください。異なるセグメントセレクターは同じメモリブロックを参照できるため、正規の順序を作成するには、プラットフォームに依存し、時間がかかる可能性があるセグメントマッピングの詳細を確認する必要があります。

于 2012-11-14T14:34:05.467 に答える
1

この議論には、ポインターの来歴というより深い概念が欠けていると思います。

原則として、一般的にポインターを比較することはできませんが、(算術演算によって)同じポインターからのポインターを比較できるはずです。たとえば、へのさまざまな呼び出しのようなブラックボックスからのポインタは、new確実に比較できません。(ここでの比較は順序付けに適用されますが、厳密に言えば、この文脈で明確に定義されていないのは平等でもあると思います。わかりません。これはmmap上記の場合をカバーします。)

それで、これが(かなり役に立たないが概念的な)答えの私の試みです:ポインターの比較は、順序演算子の適用可能性の領域での全順序です(つまり、それが未定義でない場合)。明るい面では、はい、同じ割り当てから、または単一のブロックから、所属する/来るポインターの比較を進めてください。結局のところ、それはそれを保持する必要がありp2 > p1ますT* p2 = p1 + 1;

これは、C ++のコンテナーイテレーターに起こることと似ています。2つのイテレーターが異なるコンテナーからのものである場合、それらを比較することは意味がありません。


編集: Sean Parentによるこの問題の取り上げ、https://youtu.be/mYrbivnruYw?t=3526。言い換え(1)同じコンテナのポインタしか比較できません[これは、を除いて強すぎると思いますstd::vector]。(2)std::lessポインタに使用するため、「表現」(たとえば、入力)にのみ使用されstd::setます。(3)一部のコンパイラは、ポインタの比較(void?)について文句を言います。(void *には算術演算がないため、これで問題ないと思います)。


関連資料:http ://www.open-std.org/jtc1/sc22/wg14/www/docs/n2263.htm#pointer-provenance-in-c-and-provenance-within-allocated-regions

于 2020-04-14T02:20:21.683 に答える