Namespaces
Variants

Array declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

配列型のオブジェクトを宣言します。

目次

構文

配列宣言は、 declarator が以下の形式を持つ任意の単純宣言です。

noptr-declarator [ expr  (オプション) ] attr  (オプション)
noptr-declarator - 任意の有効な declarator ですが、 * & または && で始まる場合は括弧で囲む必要があります(そうでない場合、宣言子全体が ポインタ宣言子 または 参照宣言子 として扱われます)。
expr - 整数定数式 (C++14まで) 変換された定数式 で、型は std::size_t (C++14以降) であり、ゼロより大きい値に評価されるもの
attr - (C++11以降) 属性 のリスト

形式 T a [ N ] ; の宣言は、 a を型 T N 個の連続して割り当てられたオブジェクトから構成される配列 object として宣言します。配列の要素は 0 から N - 1 まで番号付けされ、 subscript operator [] を使用して a [ 0 ] から a [ N - 1 ] のようにアクセスできます。

配列は任意の 基本型 void を除く)、 ポインタ メンバへのポインタ クラス 列挙型 、または既知のサイズを持つ他の配列(この場合、配列は多次元であると言われる)から構築できます。言い換えれば、未知のサイズの配列型を除くオブジェクト型のみが配列型の要素型となり得ます。不完全な要素型を持つ配列型も不完全型です。

制約の可能性がある 制約 (C++20以降) auto 指定子は、配列のポインタまたは参照の宣言において配列要素型として使用でき、要素型を初期化子 または関数引数 (C++14以降) から推論します。例えば、 auto ( * p ) [ 42 ] = & a ; は、 a が型 int [ 42 ] の左辺値である場合に有効です。

(C++11以降)

参照の配列や関数の配列は存在しません。

配列型に cv修飾子 を適用すると(typedefやテンプレート型操作を通じて)、修飾子は要素型に適用されますが、要素がcv修飾型である配列型は同じcv修飾を持つと見なされます。

// aとbは同じconst修飾型「5つのconst charの配列」を持つ
typedef const char CC;
CC a[5] = {};
typedef char CA[5];
const CA b = {};

new[]-expression と共に使用される場合、配列のサイズはゼロになる可能性があります;そのような配列には要素がありません:

int* p = new int[0]; // p[0] または *p へのアクセスは未定義動作
delete[] p; // クリーンアップは依然として必要

代入

配列型のオブジェクトは全体として変更することはできません:たとえ 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 を使用して配列の一時オブジェクトを構築することができます。

クラスのprvalueと同様に、配列のprvalueは評価時に 一時オブジェクト化 によってxvalueに変換されます。

(C++17以降)

配列 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++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 393 C++98 不明な境界を持つ配列へのポインタまたは参照は
関数パラメータとして使用できなかった
許可される
CWG 619 C++98 省略された場合、配列の境界は以前の宣言から
推論できなかった
推論が許可される
CWG 2099 C++98 初期化子が提供されている場合でも、配列の静的データメンバの
境界を省略できなかった
省略が許可される
CWG 2397 C++11 auto を要素型として使用できなかった 許可される

関連項目

C ドキュメント for 配列宣言