Namespaces
Variants

Array declaration

From ja.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

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

目次

構文

配列宣言とは、その宣言子が以下の形式を持つ任意の単純宣言である。

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

のリスト。 形式T a[N];T D[N]の宣言は、aDを配列オブジェクトとして宣言し、それはNN個の連続して割り当てられた型TTのオブジェクトから構成される。配列の要素には00, …, N - 1N-1の番号が付けられ、添字演算子 [] を用いて、a[0]D[0], …, a[N - 1]D[N-1]のようにアクセスできる。

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

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

(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[]式で使用される場合、配列のサイズは0であってもよく、そのような配列は要素を持たない:

int* p = new int[0]; // accessing p[0] or *p is undefined
delete[] p; // cleanup still required

実際には、簡潔な表現のため、配列要素へのポインタは配列へのポインタと呼ばれることがある:

int arr[5] {};
int *p = arr;

ppは配列arraを指すと言われるが、より厳密には、それはarraの最初の要素を指す。特に、ppがarraの最初の要素を指していない場合、それはarraの部分範囲を表すことができる。

代入

配列型のオブジェクトは全体として変更することはできません:たとえ 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 配列宣言