Array declaration
配列型のオブジェクトを宣言します。
目次構文配列宣言とは、その宣言子が以下の形式を持つ任意の単純宣言である。
のリスト。
形式 配列は、あらゆる基本型(
参照の配列や関数の配列は存在しない。 配列型にcv修飾子を(typedefやテンプレート型操作を通じて)適用すると、その修飾子は要素型に適用されるが、cv修飾された型の要素を持つ配列型は同じcv修飾を持つとみなされる。 // a and b have the same const-qualified type "array of 5 const char"
typedef const char CC;
CC a[5] = {};
typedef char CA[5];
const CA b = {};
new[]式で使用される場合、配列のサイズは0であってもよく、そのような配列は要素を持たない: int* p = new int[0]; // accessing p[0] or *p is undefined
delete[] p; // cleanup still required
実際には、簡潔な表現のため、配列要素へのポインタは配列へのポインタと呼ばれることがある: int arr[5] {};
int *p = arr;
代入配列型のオブジェクトは全体として変更することはできません:たとえ lvalues (例えば配列のアドレスを取得できる)であっても、代入演算子の左辺に現れることはできません: int a[3] = {1, 2, 3}, b[3] = {4, 5, 6}; int (*p)[3] = &a; // OK: aのアドレスを取得可能 a = b; // エラー: aは配列 struct { int c[3]; } s1, s2 = {3, 4, 5}; s1 = s2; // OK: 暗黙的に定義されたコピー代入演算子は // 配列型のデータメンバを代入可能 配列からポインタへの減衰配列型の左辺値および右辺値からポインタ型の右辺値への 暗黙変換 が存在します:これは配列の最初の要素へのポインタを構築します。この変換は、配列が配列を期待しないがポインタを期待する文脈で配列が現れるときに常に使用されます:
このコードを実行
#include <iostream> #include <iterator> #include <numeric> void g(int (&a)[3]) { std::cout << a[0] << '\n'; } void f(int* p) { std::cout << *p << '\n'; } int main() { int a[3] = {1, 2, 3}; int* p = a; std::cout << sizeof a << '\n' // 配列のサイズを出力 << sizeof p << '\n'; // ポインタのサイズを出力 // 配列は受け入れ可能だがポインタが受け入れられない場合、配列のみ使用可能 g(a); // OK: 関数は参照で配列を受け取る // g(p); // エラー for (int n : a) // OK: 範囲ベースforループで配列を使用可能 std::cout << n << ' '; // 配列の要素を出力 // for (int n : p) // エラー // std::cout << n << ' '; std::iota(std::begin(a), std::end(a), 7); // OK: beginとendは配列を受け取る // std::iota(std::begin(p), std::end(p), 7); // エラー // ポインタは受け入れ可能だが配列が受け入れられない場合、両方使用可能: f(a); // OK: 関数はポインタを受け取る f(p); // OK: 関数はポインタを受け取る std::cout << *a << '\n' // 最初の要素を出力 << *p << '\n' // 同様 << *(a + 1) << ' ' << a[1] << '\n' // 2番目の要素を出力 << *(p + 1) << ' ' << p[1] << '\n'; // 同様 } 多次元配列配列の要素型が別の配列である場合、その配列は多次元であると言います: // 2つの配列の配列、各配列は3つのintを持つ int a[2][3] = {{1, 2, 3}, // 2×3行列として見なせる {4, 5, 6}}; // 行優先レイアウトで 配列からポインタへの減衰が適用される場合、多次元配列はその最初の要素(例えば、最初の行または最初の平面)へのポインタに変換されることに注意してください:配列からポインタへの減衰は一度だけ適用されます。 int a[2]; // 2つのintの配列 int* p1 = a; // aはaの最初の要素へのポインタに減衰する int b[2][3]; // 3つのintの配列2つの配列 // int** p2 = b; // エラー: bはint**に減衰しない int (*p2)[3] = b; // bはbの最初の3要素行へのポインタに減衰する int c[2][3][4]; // 4つのintの配列3つの配列2つの配列 // int*** p3 = c; // エラー: cはint***に減衰しない int (*p3)[3][4] = c; // cはcの最初の3×4要素平面へのポインタに減衰する 境界が不明な配列配列の宣言において expr が省略された場合、宣言される型は「Tの未知のサイズの配列」となり、これは 不完全型 の一種です。ただし、 集成体初期化 を使用した宣言で使用される場合は除きます: extern int x[]; // xの型は「要素数不明のint配列」 int a[] = {1, 2, 3}; // aの型は「3要素のint配列」 配列要素は未知の境界を持つ配列にはなれないため、多次元配列は最初の次元以外では未知の境界を持つことはできません: extern int a[][2]; // 正常: 未知境界のint[2]配列の配列 extern int b[2][]; // エラー: 配列が不完全な要素型を持っています 同じスコープ内で境界が指定されたエンティティの先行宣言がある場合、省略された配列の境界はその先行宣言と同じものと見なされ、同様にクラスの静的データメンバーの定義についても同様です: extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: 境界は10 int S::y[]; // OK: 境界は10 void f() { extern int x[]; int i = sizeof(x); // エラー: 不完全なオブジェクト型 } 不明な境界を持つ配列への参照とポインタは形成できるが、 初期化や代入はできなかった (C++20まで) 初期化や代入ができるようになった (C++20以降) (既知の境界を持つ配列やそのポインタから)。Cプログラミング言語では、不明な境界を持つ配列へのポインタは既知の境界を持つ配列へのポインタと互換性があり、双方向で変換可能かつ代入可能であることに注意。 extern int a1[]; int (&r1)[] = a1; // 正常 int (*p1)[] = &a1; // 正常 int (*q)[2] = &a1; // エラー (C言語では正常) int a2[] = {1, 2, 3}; int (&r2)[] = a2; // 正常 (C++20以降) int (*p2)[] = &a2; // 正常 (C++20以降) 未知のサイズの配列へのポインタは ポインタ演算 に参加できず、 添字演算子 の左側で使用することはできませんが、間接参照は可能です。 配列右辺値配列は関数から値で返すことができず、ほとんどのキャスト式の対象にもなりませんが、型エイリアスを使用して prvalues を形成し、 brace-initialized functional cast を使用して配列の一時オブジェクトを構築することができます。
配列 xvalue は、クラスrvalueの配列メンバーに直接アクセスするか、 std::move または他のキャストやrvalue参照を返す関数呼び出しを使用することで直接形成できます。
このコードを実行
#include <iostream> #include <type_traits> #include <utility> void f(int (&&x)[2][3]) { std::cout << sizeof x << '\n'; } struct X { int i[2][3]; } x; template<typename T> using identity = T; int main() { std::cout << sizeof X().i << '\n'; // 配列のサイズ f(X().i); // OK: xvalueにバインド // f(x.i); // エラー: lvalueにバインドできない int a[2][3]; f(std::move(a)); // OK: xvalueにバインド using arr_t = int[2][3]; f(arr_t{}); // OK: prvalueにバインド f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // OK: prvalueにバインド } 出力: 24 24 24 24 24 不具合報告以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
関連項目
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||