1. ホーム
  2. c++

[解決済み] C++17以降も正しいアドレスと型を持つポインタは常に有効なポインタなのか?

2023-05-12 20:14:51

質問

<サブ (参考として この質問と回答 .)

C++17 標準以前は、次のような文が [基本.複合]/3 :

<ブロッククオート

T型のオブジェクトがアドレスAにある場合、アドレスAを値とするcv T*型のポインタは、その値がどのように得られたかに関わらず、そのオブジェクトを指すとされる。

しかし、C++17 からは、この文は が削除されました。 .

例えば私は、この文章によってこの例のコードが定義され、C++17以降ではこれが未定義の動作になっていると考えています。

 alignas(int) unsigned char buffer[2*sizeof(int)];
 auto p1=new(buffer) int{};
 auto p2=new(p1+1) int{};
 *(p1+1)=10;

C++17以前は p1+1 へのアドレスを保持します。 *p2 へのアドレスを持ち、正しい型を持っているので *(p1+1) へのポインタです。 *p2 . C++17では p1+1 末尾のポインタ であるため オブジェクトへのポインタ であり、再参照はできないと思っています。

この規格の修正の解釈は正しいのでしょうか、それとも引用文の削除を補う他のルールがあるのでしょうか。

どのように解決するのですか?

この規格の修正の解釈は正しいのでしょうか、それともこの引用文の削除を補う他のルールがあるのでしょうか。

はい、この解釈は正しいです。末尾を過ぎたポインターは、たまたまそのアドレスを指している別のポインター値に単純に変換できるわけではありません。

新しい [基本.複合]/3 は言う。

ポインタ型のすべての値は、以下のいずれかです。

(3.1) オブジェクトまたは関数へのポインタ(ポインタはオブジェクトまたは関数を指すとされる)、または

(3.2) オブジェクトの終わりを過ぎたポインタ([expr.add])、または

これらは相互に排他的です。 p1+1 は終わりを過ぎたポインタであり、オブジェクトへのポインタではありません。 p1+1 を指すのは、仮想的な x[1] にあるサイズ1の配列の p1 になるのではなく p2 . これらの2つのオブジェクトはポインタ相互変換可能ではありません。

また、非典型的な注釈もあります。

[ 注意: オブジェクトの末尾を過ぎたポインタ ([expr.add]) は、そのアドレスにあるかもしれない、そのオブジェクトの型の無関係なオブジェクトを指しているとは見なされません。[...]

であり、これは意図を明確にしています。


T.C.が多数のコメントで指摘しているように、( 特にこれ を実装しようとしたときに起こる問題の特殊なケースです。 std::vector - を実装しようとしたときに起こる問題で、それは [v.data(), v.data() + v.size()) は有効な範囲である必要があり、なおかつ vector は配列オブジェクトを生成しないので、唯一定義されたポインタ演算は、ベクトル内の任意のオブジェクトから、その仮想的な1サイズの配列の最後を過ぎて行くことでしょう。より多くのリソースについては CWG 2182 , この標準的な議論 と、2つの論文のリビジョンがあります。 P0593R0 P0593R1 (特に1.3項)を参照してください。