型
オブジェクト、参照、関数(関数テンプレートの特殊化を含む)、および式は、型と呼ばれるプロパティを持ち、それはこれらのエンティティに許可される操作を制限し、そうでなければ汎用的なビットシーケンスに意味的意味を与えます。
目次 |
[編集] 型の分類
C++の型システムは以下の型から構成されます
- 型 void(std::is_voidも参照);
| (C++11以降) |
- 算術型(std::is_arithmeticも参照)
- 整数型(cv修飾版を含む、std::is_integralも参照、整数型の同義語はinteger type)
- 型 bool;
- 文字型
- 狭い文字型
- 通常の文字型: char, signed char, unsigned char[1]
|
(C++20以降) |
- ワイド文字型: char16_t, char32_t, (C++11以降)wchar_t;
- 符号付き整数型
- 標準符号付き整数型: signed char, short, int, long, long long;
|
(C++11以降) |
- 符号なし整数型
- 標準符号なし整数型: unsigned char, unsigned short, unsigned, unsigned long, unsigned long long;
|
(C++11以降) |
- 浮動小数点型(std::is_floating_pointも参照)
- 標準浮動小数点型: float, double, long double およびそれらのcv修飾版;
| (C++23から) |
- 複合型(std::is_compoundも参照)
- 参照型(std::is_referenceも参照)
-
- オブジェクト型への左辺値参照;
- 関数型への左辺値参照;
|
(C++11以降) |
- ポインタ型(std::is_pointerも参照)
- 配列型(std::is_arrayも参照);
- 関数型(std::is_functionも参照);
- 列挙型(std::is_enumも参照);
| (C++11以降) |
- クラス型:
- 非共用体型(std::is_classも参照);
- 共用体型(std::is_unionも参照)。
- ↑ signed charとunsigned charは狭い文字型ですが、文字型ではありません。言い換えれば、狭い文字型の集合は文字型の集合の部分集合ではありません。
参照型と関数型以外のすべての非cv修飾型について、型システムはさらに3つのcv修飾版(const、volatile、const volatile)をサポートします。
[編集] その他のカテゴリ
オブジェクト型(std::is_objectも参照)は、関数型でも参照型でもなく、(cv修飾されている可能性のある)voidでもない、(cv修飾されている可能性のある)型です。
以下の型は総称してスカラ型と呼ばれます(std::is_scalarも参照)
- 算術型
- 列挙型
- ポインタ型
- メンバへのポインタ型
| (C++11以降) |
- これらの型のcv修飾版
以下の型は総称して暗黙的なライフタイム型と呼ばれます
- スカラ型
- 暗黙的なライフタイムのクラス型
- 配列型
- これらの型のcv修飾版
|
以下の型は総称して自明にコピー可能な型と呼ばれます
以下の型は総称して標準レイアウト型と呼ばれます
|
(C++11以降) |
| 型特性階層図 |
|---|
|
注: SVG画像の要素はクリック可能ですが、最初に新しいブラウザタブで図を開く必要があります。 |
[編集] 非推奨のカテゴリ
|
以下の型は総称してPOD型と呼ばれます(std::is_podも参照)
|
(C++20で非推奨) |
|
以下の型は総称して自明な型と呼ばれます(std::is_trivialも参照)
|
(C++11以降) (C++26で非推奨) |
[編集] プログラム定義型
プログラム定義の特殊化とは、C++の標準ライブラリの一部ではなく、実装によって定義されていない明示的特殊化または部分特殊化のことです。
プログラム定義型は以下の型のいずれかです。
|
(C++11以降) |
- プログラム定義の特殊化のインスタンス化。
[編集] 型の命名
名前は、以下の方法で型を参照するように宣言できます。
名前を持たない型は、C++プログラムで参照する必要があることがよくあります。その構文はtype-idとして知られています。型Tを命名するtype-idの構文は、T型の変数または関数の宣言の構文と全く同じですが、識別子を省略し、宣言文法のdecl-specifier-seqがtype-specifier-seqに制限され、新しい型はtype-idが非テンプレート型エイリアス宣言の右辺に現れる場合にのみ定義できる点が異なります。
int* p; // declaration of a pointer to int static_cast<int*>(p); // type-id is "int*" int a[3]; // declaration of an array of 3 int new int[3]; // type-id is "int[3]" (called new-type-id) int (*(*x[2])())[3]; // declaration of an array of 2 pointers to functions // returning pointer to array of 3 int new (int (*(*[2])())[3]); // type-id is "int (*(*[2])())[3]" void f(int); // declaration of a function taking int and returning void std::function<void(int)> x = f; // type template parameter is a type-id "void(int)" std::function<auto(int) -> void> y = f; // same std::vector<int> v; // declaration of a vector of int sizeof(std::vector<int>); // type-id is "std::vector<int>" struct { int x; } b; // creates a new type and declares an object b of that type sizeof(struct { int x; }); // error: cannot define new types in a sizeof expression using t = struct { int x; }; // creates a new type and declares t as an alias of that type sizeof(static int); // error: storage class specifiers not part of type-specifier-seq std::function<inline void(int)> f; // error: neither are function specifiers
宣言文法の名前が削除されたdeclarator部分はabstract-declaratorと呼ばれます。
Type-idは以下の状況で使用できます。
- キャスト式でターゲット型を指定するため;
sizeof、alignof、alignas、new、およびtypeidの引数として;- 型エイリアス宣言の右辺として;
- 関数宣言の末尾の戻り値型として;
- テンプレート型パラメータのデフォルト引数として;
- テンプレート型パラメータのテンプレート引数として;
|
(C++17まで) |
Type-idは以下の状況でいくつかの修正を加えて使用できます。
- 関数のパラメータリスト内(パラメータ名が省略されている場合)、type-idはtype-specifier-seqの代わりにdecl-specifier-seqを使用します(特に、一部の記憶域クラス指定子が許可されます);
- ユーザー定義変換関数の名前内で、抽象宣言子には関数演算子または配列演算子を含めることはできません。
| このセクションは未完成です 理由: 8.2[dcl.ambig.res] 簡潔にまとめられる場合 |
| このセクションは未完成です 理由: decltypeとautoについて言及し、リンクする |
[編集] 詳細な型指定子
詳細な型指定子は、以前に宣言されたクラス名(class、struct、またはunion)または以前に宣言されたenum名が非型宣言によって隠蔽されていた場合でも、それを参照するために使用できます。また、新しいクラス名を宣言するためにも使用できます。
詳細は詳細な型指定子を参照してください。
[編集] 静的型
プログラムのコンパイル時解析の結果として得られる式の型は、その式の静的型として知られています。静的型はプログラムの実行中に変化しません。
[編集] 動的型
何らかのglvalue式がポリモーフィックオブジェクトを参照している場合、その最も派生したオブジェクトの型は動的型として知られています。
// given struct B { virtual ~B() {} }; // polymorphic type struct D : B {}; // polymorphic type D d; // most-derived object B* ptr = &d; // the static type of (*ptr) is B // the dynamic type of (*ptr) is D
prvalue式の場合、動的型は常に静的型と同じです。
[編集] 不完全型
以下の型は不完全型です。
- 型 void(cv修飾されている可能性がある);
- 不完全に定義されたオブジェクト型:
その他のすべての型は完全です。
以下のいずれかの状況では、型Tが完全であることが求められます。
- 戻り値型が
Tまたは引数型がTの関数の定義または呼び出し; - 型
Tのオブジェクトの定義; - 型
Tの非静的クラスデータメンバの宣言; - 型
Tのオブジェクトまたは要素型がTの配列に対するnew式; - 型
Tのglvalueに適用される左辺値から右辺値への変換; - 型
Tへの暗黙的または明示的な変換; - ヌルポインタ定数またはcv修飾されている可能性のあるvoidへのポインタからの変換を除き、型T*またはT&への標準変換、
dynamic_cast、またはstatic_cast; - 型
Tの式に適用されるクラスメンバアクセス演算子; - 型
Tに適用されるtypeid、sizeof、またはalignof演算子; -
Tへのポインタに適用される算術演算子; - 基底クラスが
Tであるクラスの定義; - 型
Tの左辺値への代入; - 型
T、T&、またはT*のハンドラ。
(一般に、Tのサイズとレイアウトがわかっている必要がある場合。)
これらの状況のいずれかが翻訳単位で発生した場合、型の定義は同じ翻訳単位に現れる必要があります。そうでない場合、必須ではありません。
不完全に定義されたオブジェクト型は完了させることができます。
- クラス型(例:class X)は、翻訳単位のある時点では不完全と見なされ、後で完全と見なされるかもしれません。class X型は両方の時点で同じ型です。
struct X; // declaration of X, no definition provided yet extern X* xp; // xp is a pointer to an incomplete type: // the definition of X is not reachable void foo() { xp++; // ill-formed: X is incomplete } struct X { int i; }; // definition of X X x; // OK: the definition of X is reachable void bar() { xp = &x; // OK: type is “pointer to X” xp++; // OK: X is complete }
- 配列オブジェクトの宣言された型が不完全クラス型の配列であるため不完全である可能性があります。クラス型が翻訳単位で後で完了した場合、配列型は完全になります。これらの2つの時点での配列型は同じ型です。
- 配列オブジェクトの宣言された型が不明な境界の配列であり、したがって翻訳単位のある時点では不完全であり、後で完全になる可能性があります。これらの2つの時点での配列型(「Tの不明な境界の配列」と「N個のTの配列」)は異なる型です。
不明な境界の配列へのポインタまたは参照の型は、永続的に不完全型を指すか参照します。typedef宣言によって命名された不明な境界の配列は、永続的に不完全型を参照します。どちらの場合も、配列型を完了させることはできません。
extern int arr[]; // the type of arr is incomplete typedef int UNKA[]; // UNKA is an incomplete type UNKA* arrp; // arrp is a pointer to an incomplete type UNKA** arrpp; void foo() { arrp++; // error: UNKA is an incomplete type arrpp++; // OK: sizeof UNKA* is known } int arr[10]; // now the type of arr is complete void bar() { arrp = &arr; // OK: qualification conversion (since C++20) arrp++; // error: UNKA cannot be completed }
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 328 | C++98 | 不完全型のクラスメンバは禁止されていなかった クラス型のオブジェクトが一度も作成されなかった場合 |
非静的クラスデータメンバ 完全である必要がある |
| CWG 977 | C++98 | 列挙型がその定義内でいつ 完了するか不明瞭であった |
基底型が決定されれば 型は完了する |
| CWG 1362 | C++98 | 型T*またはT&へのユーザー定義変換はTが完全であることを要求していた |
要求されない |
| CWG 2006 | C++98 | cv修飾されたvoid型はオブジェクト型であり完全型であった | 両方のカテゴリから除外された |
| CWG 2448 | C++98 | cv非修飾型のみが整数型および浮動小数点型であるとされていた | cv修飾型も許可される |
| CWG 2630 | C++98 | クラスの定義が現れる翻訳単位の外で クラスが完全と見なされるか不明瞭であった |
クラスはその定義が このケースで 到達可能であれば |
| 完全である | C++98 | CWG 2643 不明な境界の配列へのポインタの型は |
完了できなかった(しかしそれはすでに完全である) 指し示される配列型は |
| 完了できない | C++98 | LWG 2139 | 「ユーザー定義型」の意味が不明瞭であった 代わりに「プログラム定義型」を定義し使用する |
| LWG 3119 | C++11 | クロージャ型がプログラム定義型であるか不明瞭であった | 明確化された |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 6.8.2 基本型 [basic.fundamental]
- C++20 standard (ISO/IEC 14882:2020)
- 6.8.2 基本型 [basic.fundamental]
- C++17 standard (ISO/IEC 14882:2017)
- 6.9.1 基本型 [basic.fundamental]
- C++14 standard (ISO/IEC 14882:2014)
- 3.9.1 基本型 [basic.fundamental]
- C++11 standard (ISO/IEC 14882:2011)
- 3.9.1 基本型 [basic.fundamental]
- C++98 標準 (ISO/IEC 14882:1998)
- 3.9.1 基本型 [basic.fundamental]
[編集] 関連項目
| 型特性 | 型のプロパティを照会するためのコンパイル時テンプレートベースインターフェース |
| C ドキュメント(型について)
| |
[編集] 外部リンク
| 1. | Howard HinnantのC++0x型ツリー |