宣言
宣言 (Declarations) とは、C++プログラムに名前を導入 (または再導入) する方法です。すべての宣言が実際に何かを宣言するわけではなく、エンティティの種類ごとに宣言方法が異なります。定義 (Definitions) とは、その名前で識別されるエンティティを使用するのに十分な宣言のことです。
宣言は以下のいずれかです
|
(C++11以降) |
- 空宣言 (
;) - decl-specifier-seq を持たない関数宣言
attr (オプション) declarator ; |
|||||||||
| attr | - | (C++11以降) 任意の数の属性のシーケンス |
| declarator (宣言子) | - | 関数宣言子 |
- block-declaration (ブロック内に現れることができる宣言) であり、これはさらに以下のいずれかです
| (C++11以降) |
| (C++20以降) | |
| (C++11以降) |
- 単純宣言
目次 |
[編集] 単純宣言
単純宣言とは、1つまたは複数の識別子 (通常は変数) を導入、作成し、任意で初期化する文です。
decl-specifier-seq init-declarator-list (オプション) ; |
(1) | ||||||||
attr decl-specifier-seq init-declarator-list ; |
(2) | (C++11以降) | |||||||
| decl-specifier-seq (宣言指定子シーケンス) | - | 指定子のシーケンス |
| init-declarator-list (初期化子付き宣言子リスト) | - | init-declarator (下記参照) のカンマ区切りリスト |
| attr | - | 任意の数の属性のシーケンス |
init-declarator-list は、名前付きクラスまたは名前付き列挙体を宣言する場合にのみ省略できます。
|
構造化束縛宣言も単純宣言です。 |
(C++17以降) |
init-declarator の構文は次のように定義されます
| declarator initializer | (1) | ||||||||
| declarator requires-clause (オプション) contract-specs (オプション) | (2) | ||||||||
| declarator (宣言子) | - | 宣言子 (declarator) |
| initializer (初期化子) | - | 初期化子 |
| requires-clause | - | (C++20以降) requires 節 |
| contract-specs | - | (C++26以降) 関数契約指定子のリスト |
|
requires-clause は、declarator がテンプレート化された関数を宣言する場合にのみ現れることができます。 |
(C++20以降) |
|
contract-specs は、declarator が関数または関数テンプレートを宣言する場合にのみ現れることができます。 |
(C++26以降) |
[編集] 指定子
宣言指定子 (decl-specifier-seq) は、以下の空白区切りの指定子のシーケンスで、任意の順序で指定できます
typedef指定子。存在する場合、宣言全体がtypedef 宣言となり、各宣言子はオブジェクトや関数ではなく、新しい型名を導入します。- 関数指定子 (
inline,virtual,explicit)。関数宣言でのみ許可されます。
|
(C++17以降) |
friend指定子。クラス宣言および関数宣言で許可されます。
|
(C++11以降) |
| (C++20以降) |
- 記憶域クラス指定子 (register, (C++17まで) static, thread_local, (C++11以降) extern, mutable)。記憶域クラス指定子は1つだけ許可されますが、thread_local は extern または static と一緒に現れることができます(C++11以降)。
- 型指定子 (type-specifier-seq) は、型を名付ける指定子のシーケンスです。宣言によって導入されるすべてのエンティティの型は、この型であり、任意で宣言子 (下記参照) によって修飾されます。この指定子のシーケンスは type-id でも使用されます。type-specifier-seq に含まれるのは以下の指定子のみで、順序は任意です
| (C++11以降) | |
| (C++26以降) |
- 以前に宣言されたクラス名 (任意で修飾)
- 以前に宣言された enum 名 (任意で修飾)
- 以前に宣言された typedef-name または 型エイリアス(C++11以降) (任意で修飾)
- テンプレート引数を持つテンプレート名 (任意で修飾、任意でテンプレート曖昧性解決子を使用)
|
(C++17以降) |
-
- decl-specifier-seq では、以下の例外を除き、1つの型指定子のみが許可されます
- const は、それ自体を除く任意の型指定子と組み合わせることができます。
- volatile は、それ自体を除く任意の型指定子と組み合わせることができます。
- signed または unsigned は、char, long, short, または int と組み合わせることができます。
- short または long は int と組み合わせることができます。
- long は double と組み合わせることができます。
|
(C++11以降) |
属性は decl-specifier-seq 内に現れることができ、その場合、先行する指定子によって決定される型に適用されます。
decl-specifier-seq 内での任意の指定子の繰り返し、たとえば const static const や virtual inline virtual はエラーですが、long が2回現れることは許可されます(C++11以降)。
[編集] 宣言子
init-declarator-list S D1, D2, D3; の各 init-declarator は、同じ指定子を持つスタンドアロンの宣言であるかのように処理されます: S D1; S D2; S D3;。
各宣言子は、オブジェクト、参照、関数、または (typedef 宣言の場合) 型エイリアスを正確に1つ導入します。その型は decl-specifier-seq によって提供され、宣言子内の & (への参照) や [] (の配列) や () (を返す関数) のような演算子によって任意で修飾されます。これらの演算子は、以下に示すように再帰的に適用できます。
declarator (宣言子) は以下のいずれかです
| unqualified-id attr (オプション) | (1) | ||||||||
| qualified-id attr (オプション) | (2) | ||||||||
... identifier attr (オプション) |
(3) | (C++11以降) | |||||||
* attr (オプション) cv (オプション) declarator |
(4) | ||||||||
nested-name-specifier * attr (オプション) cv (オプション) declarator |
(5) | ||||||||
& attr (オプション) declarator |
(6) | ||||||||
&& attr (オプション) declarator |
(7) | (C++11以降) | |||||||
noptr-declarator [ constexpr (オプション) ] attr (オプション) |
(8) | ||||||||
noptr-declarator ( parameter-list ) cv (オプション) ref (オプション) except (オプション) attr (オプション) |
(9) | ||||||||
D を C のメンバで、型が decl-specifier-seq S によって決定されるものへのポインタとして宣言します。nested-name-specifier は、名前とスコープ解決演算子 :: のシーケンスです。|
すべての場合において、attr は属性のオプションのシーケンスです。識別子の直後に現れる場合、宣言されるオブジェクトに適用されます。 |
(C++11以降) |
cv は const と volatile 修飾子のシーケンスで、各修飾子はシーケンス内に最大1回現れることができます。
| このセクションは未完成です 理由: 宣言の名前隠蔽ルールを説明するため。変数/関数宣言が同じ名前のクラス (ただし typedef ではない) をどのように隠蔽するか |
[編集] 備考
block-declaration がブロック内に現れ、宣言によって導入された識別子が以前に外側のブロックで宣言されていた場合、外側の宣言はブロックの残りの部分で隠蔽されます。
宣言が自動記憶域期間を持つ変数を導入する場合、その宣言文が実行されるときに初期化されます。ブロック内で宣言されたすべての自動変数は、ブロックからの脱出時に (ブロックからの脱出方法に関わらず: 例外、goto、または終端への到達)、初期化の順序とは逆の順序で破棄されます。
[編集] 例
注意: この例は、いくつかの複雑な宣言が言語文法に基づいてどのように解析されるかを示しています。その他の一般的なニーモニックには、スパイラルルール、内側から外側へ読む、宣言は使用を反映するなどがあります。また、https://cdecl.org には自動パーサーがあります。
#include <type_traits> struct S { int member; // decl-specifier-seq is "int" // declarator is "member" } obj, *pObj(&obj); // decl-specifier-seq is "struct S { int member; }" // declarator "obj" declares an object of type S // declarator "*pObj" declares a pointer to S, // and initializer "(&obj)" initializes it int i = 1, *p = nullptr, f(), (*pf)(double); // decl-specifier-seq is "int" // declarator "i" declares a variable of type int, // and initializer "= 1" initializes it // declarator "*p" declares a variable of type int*, // and initializer "= nullptr" initializes it // declarator "f()" declares (but doesn't define) // a function taking no arguments and returning int // declarator "(*pf)(double)" declares a pointer to function // taking double and returning int int (*(*var1)(double))[3] = nullptr; // decl-specifier-seq is "int" // declarator is "(*(*var1)(double))[3]" // initializer is "= nullptr" // 1. declarator "(*(*var1)(double))[3]" is an array declarator: // Type declared is: "(*(*var1)(double))" array of 3 elements // 2. declarator "(*(*var1)(double))" is a pointer declarator: // Type declared is: "(*var1)(double)" pointer to array of 3 elements // 3. declarator "(*var1)(double)" is a function declarator: // Type declared is: "(*var1)" function taking "(double)", // returning pointer to array of 3 elements. // 4. declarator "(*var1)" is a pointer declarator: // Type declared is: "var1" pointer to function taking "(double)", // returning pointer to array of 3 elements. // 5. declarator "var1" is an identifier. // This declaration declares the object var1 of type "pointer to function // taking double and returning pointer to array of 3 elements of type int" // The initializer "= nullptr" provides the initial value of this pointer. // C++11 alternative syntax: auto (*var2)(double) -> int (*)[3] = nullptr; // decl-specifier-seq is "auto" // declarator is "(*var2)(double) -> int (*)[3]" // initializer is "= nullptr" // 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator: // Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]" // ... int main() { static_assert(std::is_same_v<decltype(var1), decltype(var2)>); }
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 482 | C++98 | 再宣言の宣言子は修飾できなかった | 修飾された宣言子が許可された |
| CWG 569 | C++98 | 単独のセミコロンは有効な宣言ではなかった | それは空宣言であり、 効果はない |
| CWG 1830 | C++98 | decl-specifier-seq 内での関数指定子の繰り返しが許可されていた | 繰り返しは禁止された |
[編集] 関連項目
| 宣言についてのC言語のドキュメント
|