2021 年 3 月 20 日更新:
これと同じ質問が最近Redditで行われましたが、このエイリアシング ルールが考慮されていないため、私の元の回答には欠陥があることが指摘されました。
プログラムが、次の型のいずれとも似ていない型の glvalue を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。
- オブジェクトの動的タイプ、
- オブジェクトの動的型に対応する符号付きまたは符号なしの型、または
- char、unsigned char、または std :: byte 型。
類似性のルールの下では、これら 2 つの配列型は上記のいずれのケースでも類似していないため、3D 配列を介して 1D 配列にアクセスすることは技術的に未定義の動作です。(これは間違いなく、実際には、ほとんどのコンパイラ/ターゲットでほぼ確実に機能する状況の 1 つです)
元の回答の参照は、古い C++11 ドラフト標準を参照していることに注意してください
元の答え:
reinterpret_cast
参照の
標準では、型の左辺値は、へのポインターがへのポインターになることができるT1
場合、reinterpret_cast
への参照になることができると述べています(§5.2.10/11):T2
T1
reinterpret_cast
T2
型の左辺値式は、reinterpret_cast を使用して型 "pointer to" の式を型 "pointer to" に明示的に変換できる場合T1
、型 "reference to" にキャストできます。T2
T1
T2
したがって、 aint(*)[N]
を に変換できるかどうかを判断する必要がありint(*)[I][J][K]
ます。
reinterpret_cast
ポインターの
へのポインターは、との両方が標準レイアウト型であり、 (§5.2.10/7)より厳密なアラインメント要件がない場合、へのポインターにするT1
ことができます。reinterpret_cast
T2
T1
T2
T2
T1
型「T1 へのポインター」の prvalue v が型「cv T2 へのポインター」に変換されるとき、結果は、とのstatic_cast<cv T2*>(static_cast<cv void*>(v))
両方が標準レイアウト型 (3.9) であり、 の整列要件が の整列要件よりも厳密でない場合、またはいずれかのタイプが無効な場合。T1
T2
T2
T1
int[N]
とはint[I][J][K]
標準レイアウト タイプですか?
int
はスカラー型であり、スカラー型の配列は標準レイアウト型と見なされます(§3.9/9)。
スカラー型、標準レイアウト クラス型 (条項 9)、そのような型の配列、およびこれらの型の cv 修飾バージョン (3.9.3) は、まとめて標準レイアウト型と呼ばれます。
int[I][J][K]
には、より厳密なアラインメント要件はありませんint[N]
。
演算子の結果はalignof
、完全なオブジェクト型 (§3.11/2) のアラインメント要件を示します。
演算子の結果はalignof
、完全なオブジェクトの場合の型のアラインメント要件を反映しています。
ここでの 2 つの配列は、他のオブジェクトのサブオブジェクトではないため、完全なオブジェクトです。配列に適用するalignof
と、要素タイプのアライメント要件が得られます (§5.3.6/3):
alignof
を配列型に適用すると、結果は要素型の位置合わせになります。
したがって、両方の配列型のアライメント要件は同じです。
これにより、reinterpret_cast
有効で同等のものが次のようになります。
int (&arr3d)[I][J][K] = *reinterpret_cast<int (*)[I][J][K]>(&arr1d);
ここで*
、 および&
は組み込み演算子であり、次と同等です。
int (&arr3d)[I][J][K] = *static_cast<int (*)[I][J][K]>(static_cast<void*>(&arr1d));
static_cast
終えたvoid*
static_cast
tovoid*
は、標準の変換 (§4.10/2) で許可されています。
「cv へのポインター」T
型T
( はオブジェクト型) の prvalue は、「cv void へのポインター」型の prvalue に変換できます。「cv へのポインター」を「cv void へのポインター」に変換した結果は、オブジェクトが型の最も派生したオブジェクト (1.8) であるかのように、T
型のオブジェクトが存在する格納場所の先頭を指します(つまり、 、基本クラスのサブオブジェクトではありません)。T
T
static_cast
toが許可されますint(*)[I][J][K]
(§5.2.9/13):
「cv1 へのポインター」型の prvalue は、「 void
cv2 へのポインター」型の prvalue に変換できますT
。ここT
で、 はオブジェクト型であり、cv2 は cv1 と同じ cv-qualification またはそれより大きい cv-qualification です。
だからキャストは大丈夫です!しかし、新しい配列参照を介してオブジェクトにアクセスしても大丈夫でしょうか?
配列要素へのアクセス
配列に対して配列添字を実行するのは(§5.2.1/1)arr3d[E2]
と同等です。*((E1)+(E2))
次の配列添字について考えてみましょう。
arr3d[3][2][1]
まず、arr3d[3]
と同等*((arr3d)+(3))
です。左辺値arr3d
は、配列からポインターへの変換を受けて、int(*)[2][1]
. この変換を行うために、基になる配列が正しい型でなければならないという要件はありません。次に、ポインター値にアクセスし (これは §3.10 で問題ありません)、値 3 がそれに追加されます。このポインター演算も問題ありません (§5.7/5):
ポインターオペランドと結果の両方が同じ配列オブジェクトの要素を指している場合、または配列オブジェクトの最後の要素の 1 つ後ろを指している場合、評価はオーバーフローを生成しません。それ以外の場合、動作は未定義です。
この this ポインタは逆参照されてint[2][1]
. これは、次の 2 つの添字に対して同じプロセスを経てint
、適切な配列インデックスで最終的な左辺値になります。*
(§5.3.1/1)の結果による左辺値です。
単項 * 演算子は間接参照を実行します。それが適用される式は、オブジェクト型へのポインター、または関数型へのポインターであり、結果は、式が指すオブジェクトまたは関数を参照する左辺値になります。
int
左辺値も型であるため、この左辺値を介して実際のオブジェクトにアクセスすることはまったく問題ありint
ません (§3.10/10):
プログラムが、次の型以外の glvalue を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。
だから私が何かを見逃していない限り。このプログラムはよく定義されていると思います。