型
オブジェクト、参照、関数 (関数テンプレートの特殊化を含む) および式は、型と呼ばれる性質を持ちます。 これは、それらのエンティティに対して許される操作の制限と、ビットの並びに対する意味論上の意味付けの、両方を表します。
目次 |
[編集] 型の分類
C++の型システムは以下の型から構成されます。
- 基本型 (std::is_fundamental も参照してください)
- void 型 (std::is_void も参照してください)
- std::nullptr_t 型 (C++11以上) (std::is_null_pointer も参照してください)
- 算術型 (std::is_arithmetic も参照してください)
- 浮動小数点型 (float、 double、 long double) (std::is_floating_point も参照してください)
- 整数型 (std::is_integral も参照してください)
- bool 型
- 文字型
- ナロー文字型
- 普通の文字型 (char、 signed char、 unsigned char)
- char8_t 型
- ワイド文字型 (char16_t、 char32_t、 wchar_t)
- 符号付き整数型 (short int、 int、 long int、 long long int)
- 符号なし整数型 (unsigned short int、 unsigned int、 unsigned long int、 unsigned long long int)
- 複合型 (std::is_compound も参照してください)
- 参照型 (std::is_reference も参照してください)
- 左辺値参照型 (std::is_lvalue_reference も参照してください)
- オブジェクトへの左辺値参照型
- 関数への左辺値参照型
- 右辺値参照型 (std::is_rvalue_reference も参照してください)
- オブジェクトへの右辺値参照型
- 関数への右辺値参照型
- ポインタ型 (std::is_pointer も参照してください)
- メンバへのポインタ型 (std::is_member_pointer も参照してください)
- データメンバへのポインタ型 (std::is_member_object_pointer も参照してください)
- メンバ関数へのポインタ型 (std::is_member_function_pointer も参照してください)
- 配列型 (std::is_array も参照してください)
- 関数型 (std::is_function も参照してください)
- 列挙型 (std::is_enum も参照してください)
- クラス型
- 非共用体クラス型 (std::is_class も参照してください)
- 共用体型 (std::is_union も参照してください)
参照と関数以外のすべての型につ���て、その型の3種類の cv 修飾されたバージョン (const、 volatile、 const volatile) が追加でサポートされます。
型はその性質に基づいて様々なカテゴリに分類されます。
- オブジェクト型は、関数型でなく、参照型でなく、 void 型でない型 (またはそれらの cv 修飾された型) です (std::is_object も参照してください)。
- スカラー型は、配列型でなく、クラス型でない、オブジェクト型 (またはそれらの cv 修飾された型) です (std::is_scalar も参照してください)
- トリビアルな型 (std::is_trivial を参照してください)、 POD 型 (std::is_pod を参照してください)、リテラル型 (std::is_literal_type を参照してください)、およびその他の型は型特性ライブラリに、または名前付き型要件として掲載されています。
[編集] 型の名前付け
名前は以下の方法で型を参照するように宣言できます。
C++プログラム内でしばしば名前を持たない型を参照する必要があります。 そのための構文は type-id と呼ばれます。 型 T を表す type-id の構文は正確に型 T の変数または関数の宣言の構文です。 ただし識別子は省略され、宣言の文法の decl-specifier-seq は type-specifier-seq に制約され、 type-id が非テンプレート型エイリアス宣言の右側に現れる場合にのみ新しい型が定義されます。
int* p; // int へのポインタの宣言 static_cast<int*>(p); // type-id は「int*」 int a[3]; // int 3個の配列の宣言 new int[3]; // type-id は「int[3]」 (new-type-id と呼ばれます) int (*(*x[2])())[3]; // int 3個の配列へのポインタを返す関数への // ポインタ2個の配列の宣言 new (int (*(*[2])())[3]); // type-id は「int (*(*[2])())[3]」 void f(int); // int を取り void を返す関数の宣言 std::function<void(int)> x = f; // 型テンプレート引数は type-id 「void(int)」 std::function<auto(int) -> void> y = f; // 同上 std::vector<int> v; // int のベクタの宣言 sizeof(std::vector<int>); // type-id は「std::vector<int>」 struct { int x; } b; // 新しい型を作成し、その型のオブジェクト b を宣言 sizeof(struct{ int x; }); // エラー、 sizeof 式の中で新しい型を宣言することはできません using t = struct { int x; }; // 新しい型を作成し、その型のエイリアスとして t を宣言 sizeof(static int); // エラー、 type-specifier-seq の一部でない記憶域クラス指定子 std::function<inline void(int)> f; // エラー、いずれも関数指定子ではありません
宣言の文法から名前を除いた宣言子の部分は抽象宣言子と呼ばれます。
type-id は以下の状況で使用することができます。
- キャスト式のターゲット型を指定するため。
- sizeof、 alignof、 alignas、 new、 typeid の引数として。
- 型エイリアス宣言の右側で。
- 関数宣言の後置戻り値型として。
- テンプレート型引数のデフォルト引数として。
- テンプレート型引数に対するテンプレートの実引数として。
- 動的例外指定で。
type-id は以下の状況で多少変更して使用することができます。
- 関数の仮引数リストにおいて (引数名が省略されたときに)、 type-id は type-specifier-seq の代わりに decl-specifier-seq を使用します (特に、いくつかの記憶域クラス指定子が使用できます)。
- ユーザ定義変換関数の名前において、抽象宣言子は関数または配列演算子を含むことができません。
This section is incomplete Reason: 8.2[dcl.ambig.res] if it can be compactly summarized |
This section is incomplete Reason: mention and link to decltype and auto |
[編集] 複雑型指定子
複雑型指定子は、以前に宣言されたクラス (クラス、構造体、または共用体) の名前、または以前に宣言された列挙の名前を参照するために使用できます (たとえその名前が非型宣言によって隠蔽されている場合であっても)。 これらは新しいクラスの名前を宣言するために使用されることもあります。
詳細については複雑型指定子を参照してください。
[編集] 静的な型
プログラムのコンパイル時の解析の結果である式の型は、その式の静的な型と呼ばれます。 静的な型はプログラムの実行中に変更されません。
[編集] 動的な型
何らかの glvalue 式が多相オブジェクトを参照する場合、その最も派生した型は動的な型と呼ばれます。
struct B { virtual ~B() {} }; // 多相型 struct D: B {}; // 多相型 D d; // 最も派生したオブジェクト B* ptr = &d; // (*ptr) の静的な型は B です。 // (*ptr) の動的な型は D です。
prvalue 式の場合、動的な型は常に静的な型と同じです。
[編集] 不完全型
以下の型は不完全型です。
- void 型 (または cv 修飾された void 型)。
- 宣言されているけれども定義されていないクラス型 (例えば前方宣言によって)。
- サイズが未知な配列。
- 要素が不完全型の配列。
- 列挙型の、ベースとなる型がまだ決定していない場所から見た場合。
以下の文脈はいずれもクラス T
が完全であることを要求します。
-
T
を戻り値の型または引数の型に持つ関数の定義、またはそのような関数の呼び出し。 -
T
型のオブジェクトの定義。 -
T
型の非静的クラスデータメンバの宣言。 -
T
型のオブジェクトまたは要素の型がT
である配列に対する new 式。 -
T
型の glvalue に適用される左辺値から右辺値への変換。 -
T
型への暗黙または明示的な変換。 -
T*
またはT&
型への標準変換、 dynamic_cast、または static_cast (ヌルポインタ定数または void へのポインタから変換するときは除きます)。 -
T
型の式に適用されるクラスメンバアクセス演算子。 -
T
型に適用される typeid、 sizeof、または alignof 演算子。 -
T
へのポインタに適用される算術演算子。 -
T
を基底クラスに持つクラスの定義。 -
T
型の左辺値への代入。 -
T
、T&
またはT*
型の例外に対する catch 節。
(全般的に、 T
のサイズやレイアウトが判明していなければならないときです。)
ある翻訳単位でこれらのいずれかの状況が発生した場合は、その型の定義が同じ翻訳単位内に現れなければなりません。 そうでない場合は、それは要求されません。
不完全に定義されたオブジェクト型は完全にすることができます。
- クラス型 (class X など) は、翻訳単位内のある地点で不完全であり、その後、完全となることがあります。 型 class X はどちらの地点でも同じ型です。
class X; // X は不完全型です。 extern X* xp; // xp は不完全型へのポインタです。 void foo() { xp++; // ill-formed、 X は不完全です。 } struct X { int i; }; // ここで X は完全型になります。 X x; void bar() { xp = &x; // OK、型は「X へのポインタ」です。 xp++; // OK、 X は完全です。 }
- 任意のオブジェクトの宣言された型は、不完全なクラスの配列であってもよく、そのため不完全であることがあります。 そのクラス型がその翻訳単位内で後に完全になる場合、その配列型は完全になります。 それら2つの地点におけるその配列型は同じ型です。
- 配列オブジェクトの宣言された型はサイズが未知な配列であってもよく、そのため翻訳単位内のある地点で不完全であり後に完全となることがあります。 それら2つの地点におけるその配列型 (「境界が未知な T の配列」と「N 個の T の配列」) は異なる型です。
This section is incomplete Reason: examples for completing the incomplete array types from §3.9[basic.types]/6 |
- 境界が未知な配列または typedef 宣言によって境界が未知な配列と定義された型へのポインタの型は、完全になることはできません。
[編集] 関連項目
型 の C言語リファレンス
|