配列宣言
配列型のオブジェクトを宣言します。
目次 |
[編集] 構文
配列宣言は、宣言子が以下の形式をとる単純な宣言です。
noptr-declarator [expr (オプション)] attr (オプション) |
|||||||||
| noptr-declarator | - | 任意の有効な宣言子ですが、*、&、または&&で始まる場合は括弧で囲む必要があります(そうしないと、宣言子全体がポインタ宣言子または参照宣言子として扱われます)。 |
| expr | - | 整数定数式(C++14まで)変換された定数式 (型はstd::size_t)(C++14以降)。0より大きい値に評価されます。 |
| attr | - | (C++11以降) 属性のリスト |
T a[N];という形式の宣言は、aを、型TのN個の連続して割り当てられたオブジェクトからなる配列オブジェクトとして宣言します。配列の要素には0からN - 1までの番号が付けられており、添字演算子 [] を使用して、a[0]、…、a[N - 1]のようにアクセスできます。
配列は、任意の基本型(voidを除く)、ポインタ、メンバへのポインタ、クラス、列挙型、または既知の境界を持つ他の配列から構成できます(この場合、配列は多次元配列と呼ばれます)。言い換えれば、要素の型になれるのは、境界不明の配列型を除くオブジェクト型のみです。要素の型が不完全な配列型も不完全な型です。
|
配列へのポインタまたは参照の宣言では、制約付きの可能性がある(C++20以降) |
(C++11以降) |
参照の配列や関数の配列はありません。
配列型に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[]-expressionで使用される場合、配列のサイズはゼロになることがあります。このような配列には要素がありません。
int* p = new int[0]; // accessing p[0] or *p is undefined delete[] p; // cleanup still required
[編集] 代入
配列型のオブジェクトは全体として変更できません。それらは左辺値(例えば、配列のアドレスを取得できる)ですが、代入演算子の左側に現れることはできません。
int a[3] = {1, 2, 3}, b[3] = {4, 5, 6}; int (*p)[3] = &a; // okay: address of a can be taken a = b; // error: a is an array struct { int c[3]; } s1, s2 = {3, 4, 5}; s1 = s2; // okay: implicitly-defined copy assignment operator // can assign data members of array type
[編集] 配列からポインタへの変換 (Array-to-pointer decay)
配列型の左辺値および右辺値からポインタ型の右辺値への暗黙の変換があります。これは配列の最初の要素へのポインタを構築します。この変換は、配列が予期されないがポインタが予期されるコンテキストで配列が出現するときに常に使用されます。
#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' // prints size of array << sizeof p << '\n'; // prints size of a pointer // where arrays are acceptable, but pointers aren't, only arrays may be used g(a); // okay: function takes an array by reference // g(p); // error for (int n : a) // okay: arrays can be used in range-for loops std::cout << n << ' '; // prints elements of the array // for (int n : p) // error // std::cout << n << ' '; std::iota(std::begin(a), std::end(a), 7); // okay: begin and end take arrays // std::iota(std::begin(p), std::end(p), 7); // error // where pointers are acceptable, but arrays aren't, both may be used: f(a); // okay: function takes a pointer f(p); // okay: function takes a pointer std::cout << *a << '\n' // prints the first element << *p << '\n' // same << *(a + 1) << ' ' << a[1] << '\n' // prints the second element << *(p + 1) << ' ' << p[1] << '\n'; // same }
[編集] 多次元配列
配列の要素型が別の配列である場合、その配列は多次元であると言われます。
// array of 2 arrays of 3 int each int a[2][3] = {{1, 2, 3}, // can be viewed as a 2 × 3 matrix {4, 5, 6}}; // with row-major layout
配列からポインタへの変換が適用される場合、多次元配列は最初の要素へのポインタ(例えば、最初の行または最初の平面へのポインタ)に変換されます。配列からポインタへの変換は一度だけ適用されます。
int a[2]; // array of 2 int int* p1 = a; // a decays to a pointer to the first element of a int b[2][3]; // array of 2 arrays of 3 int // int** p2 = b; // error: b does not decay to int** int (*p2)[3] = b; // b decays to a pointer to the first 3-element row of b int c[2][3][4]; // array of 2 arrays of 3 arrays of 4 int // int*** p3 = c; // error: c does not decay to int*** int (*p3)[3][4] = c; // c decays to a pointer to the first 3 × 4-element plane of c
[編集] 境界不明の配列
配列の宣言でexprが省略された場合、宣言される型は「Tの境界不明の配列」であり、これは集約初期化子を持つ宣言で使用される場合を除き、一種の不完全型です。
extern int x[]; // the type of x is "array of unknown bound of int" int a[] = {1, 2, 3}; // the type of a is "array of 3 int"
配列の要素は境界不明の配列にはなれないため、多次元配列は最初の次元以外の次元で境界を不明にすることはできません。
extern int a[][2]; // okay: array of unknown bound of arrays of 2 int extern int b[2][]; // error: array has incomplete element type
同じスコープ内に、境界が指定されたエンティティの先行する宣言がある場合、省略された配列境界はその先行する宣言と同じであると見なされ、クラスの静的データメンバの定義も同様です。
extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: bound is 10 int S::y[]; // OK: bound is 10 void f() { extern int x[]; int i = sizeof(x); // error: incomplete object type }
境界不明の配列への参照とポインタを形成できますが、既知の境界を持つ配列と配列へのポインタから初期化または代入することはできません。(C++20まで)既知の境界を持つ配列と配列へのポインタから初期化または代入できます。(C++20以降)。Cプログラミング言語では、境界不明の配列へのポインタは既知の境界を持つ配列へのポインタと互換性があり、両方向に変換および代入可能です。
extern int a1[]; int (&r1)[] = a1; // okay int (*p1)[] = &a1; // okay int (*q)[2] = &a1; // error (but okay in C) int a2[] = {1, 2, 3}; int (&r2)[] = a2; // okay (since C++20) int (*p2)[] = &a2; // okay (since C++20)
境界不明の配列へのポインタは、ポインタ算術に参加できず、添字演算子の左側で使用することはできませんが、逆参照することはできます。
[編集] 配列の右辺値 (Array rvalues)
配列は関数から値で返したり、ほとんどのキャスト式のターゲットになったりすることはできませんが、波括弧初期化関数キャストを使用して配列一時オブジェクトを構築するために型エイリアスを使用することで、配列のprvaluesを形成できます。
|
クラストprvaluesと同様に、配列prvaluesは評価されるときに一時的な実体化によってxvaluesに変換されます。 |
(C++17以降) |
配列のxvaluesは、クラスの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'; // size of the array f(X().i); // okay: binds to xvalue // f(x.i); // error: cannot bind to lvalue int a[2][3]; f(std::move(a)); // okay: binds to xvalue using arr_t = int[2][3]; f(arr_t{}); // okay: binds to prvalue f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // okay: binds to prvalue }
出力
24 24 24 24 24
[編集] Defect reports (欠陥報告)
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 393 | C++98 | 境界不明の配列へのポインタまたは参照は 関数パラメータになれなかった |
許可 |
| CWG 619 | C++98 | 省略された場合、配列の境界は 以前の宣言から推論できなかった |
推論が可能になった |
| CWG 2099 | C++98 | 配列の静的データメンバの境界は 初期化子が提供されていても省略できなかった |
省略が可能になった |
| CWG 2397 | C++11 | autoを要素型として使用できなかった | 許可 |
[編集] 関連項目
| Cドキュメント: 配列宣言
|