記憶域クラス指定子
記憶域クラス指定子は名前の宣言の構文の decl-specifier-seq の一部です。 名前のスコープと共に、その名前の2つの独立した性質、記憶域期間とリンケージを制御します。
|
(C++11未満) |
|
(C++17未満) |
|
(C++11以上) |
mutable
ー 記憶域期間やリンケージには影響しません。 説明は const/volatile を参照してください。
ひとつの宣言に記憶域クラス指定子はひとつだけ現れることができます。 ただし thread_local
は static
または extern
と組み合わせることができます。 (C++11以上)
目次 |
[編集] 性質
1) auto 指定子はブロックスコープまたは関数の引数リストで宣言されるオブジェクトに対してのみ使用できます。 自動記憶域期間を表します (これらの種類の宣言に対するデフォルトです)。 このキーワードのこの意味は C++11 で変更されました。
|
(C++11未満) |
2) register 指定子はブロックスコープまたは関数の引数リストで宣言されるオブジェクトに対してのみ使用できます。 自動記憶域期間を表します (これらの種類の宣言に対するデフォルトです)。 また、このキーワードの存在はこの辺数の値を CPU のレジスタ内に格納するための最適化に対するヒントとして使用されることもあります。 このキーワードは C++11 で非推奨になりました。
|
(C++17未満) |
static
指定子はオブジェクトの宣言 (関数の引数リスト内は除きます)、関数の宣言 (ブロックスコープのものは除きます)、および無名共用体の宣言でのみ使用できます。 クラスメンバの宣言で使用されるときは、静的メンバを宣言します。 オブジェクトの宣言で使用されるときは、静的記憶域期間を指定します (thread_local
を伴う場合は除きます)。 名前空間スコープの宣言で使用されるときは、内部リンケージを指定します。extern
指定子は変数および関数 (クラスメンバまたは関数の引数は除きます) の宣言でのみ使用できます。 外部リンケージを指定します。 厳密には記憶域期間に影響しませんが、自動記憶域期間のオブジェクトの定義では使用できないため、 extern
なオブジェクトはすべて静的またはスレッド期間を持ちます。 また、 extern
を使用し初期化子を持たない変数宣言は、定義ではありません。
5)
thread_local キーワードは名前空間スコープで宣言されるオブジェクト、ブロックスコープで宣言されるオブジェクト、および静的データメンバに対してのみ使用できます。 オブジェクトがスレッド記憶域期間を持つことを表します。 内部リンケージまたは外部リンケージを指定するために (常に外部リンケージを持つ静的データメンバの場合は除きます) static または extern を組み合わせることができますが、追加の static は記憶域期間に影響しません。 |
(C++11以上) |
[編集] 記憶域期間
プログラム内のすべてのオブジェクトは以下の記憶域期間のいずれかを持ちます。
- 自動記憶域期間。 オブジェクトのための記憶域は囲っているコードブロックの始まりで確保され終わりで解放されます。 すべてのローカルなオブジェクトは、
static
、extern
またはthread_local
宣言されたものを除いて、この記憶域期間を持ちます。
- 自動記憶域期間。 オブジェクトのための記憶域は囲っているコードブロックの始まりで確保され終わりで解放されます。 すべてのローカルなオブジェクトは、
(C++11以上) |
[編集] リンケージ
オブジェクト、参照、関数、型、テンプレート、名前空間、または値を表す名前は、リンケージを持つことがあります。 名前がリンケージを持つ場合、その名前は別のスコープ内の宣言によって導入された同じ名前と同じエンティティを参照します。 同じ名前を持つ変数、関数、または他のエンティティが複数のスコープで宣言されているけれども十分なリンケージを持たない場合は、そのエンティティの実体が複数生成されます。
以下のリンケージが認識されます。
[編集] リンケージなし
名前はそれが存在するスコープからのみ参照できます。
ブロックスコープで宣言される以下の名前はいずれもリンケージなしです。
- 明示的に
extern
宣言されていない変数 (static
修飾子に関係なく)。 - ローカルクラスおよびそのメンバ関数。
- typedef、列挙、列挙子などの、ブロックスコープで宣言されるその他の名前。
[編集] 内部リンケージ
名前は現在の翻訳単位内のすべてのスコープから参照できます。
名前空間スコープで宣言される以下の名前はいずれも内部リンケージを持ちます。
-
static
宣言された変数、変数テンプレート (C++14以上)、関数、関数テンプレート。 -
extern
宣言されておらず、以前に外部リンケージを持つように宣言されていない、非 volatile、非テンプレート (C++14以上)、非インライン (C++17以上)な、エクスポートされていない (C++20以上)、 const 修飾された変数 (constexpr を含みます)。 - 無名共用体のデータメンバ。
さらに、無名名前空間または無名名前空間内の名前空間で宣言されたすべての名前は、たとえ明示的に |
(C++11以上) |
[編集] 外部リンケージ
名前は他の翻訳単位内のスコープから参照できます。 外部リンケージを持つ変数および関数は言語リンケージも持ちます。 これは異なるプログラミング言語で書かれた翻訳単位のリンクを可能とします。
後述の例外を除いて、名前空間スコープで宣言される以下の名前はいずれも外部リンケージを持ちます。
- 上にない変数および関数 (つまり、
static
宣言されていない関数、static
宣言されていない名前空間スコープの非 const な変数、およびextern
宣言されたあらゆる変数)。 - 列挙。
- クラス、そのメンバ関数、静的データメンバ (const もそうでないものも)、ネストしたクラスおよび列挙、およびクラス本体内の friend 宣言を用いて最初に導入された関数の名前。
- 上にないすべてのテンプレートの名前 (つまり、
static
宣言された関数テンプレート以外のもの)。
ブロックスコープで最初に宣言された以下の名前はいずれも外部リンケージを持ちます。
-
extern
宣言された変数の名前。 - 関数の名前。
無名名前空間内の名前をその名前空間内で明示的に |
(C++11未満) |
名前が名前付きモジュール内で宣言され、エクスポートされていない場合は、その名前はモジュールリンケージを持ちます。 |
(C++20以上) |
モジュールリンケージ名前は同じモジュールユニット内または同じ名前付きモジュールの他の翻訳単位内のスコープからのみ参照できます。 名前空間スコープで宣言された名前は、それが名前付きモジュール内で宣言され、エクスポートされていない場合は、内部リンケージを持たず、モジュールリンケージを持ちます。 |
(C++20以上) |
[編集] 静的ローカル変数
static
または thread_local
(C++11以上) 指定子を使用してブロックスコープで宣言された変数は、静的またはスレッド (C++11以上)記憶域期間を持ちますが、制御がその宣言を最初に通過したときに初期化されます (その初期化がゼロ初期化または定数初期化の場合を除きます。 それらはブロックに最初に入る前に行うことができます)。 その後のすべての呼び出しにおいて、宣言はスキップされます。
初期化が例外を投げた場合、変数は初期化されたとみなされず、制御がその宣言を次回通過したときに再度初期化が試みられます。
変数を初期化中のブロックに初期化が再帰的に入った場合、動作は未定義です。
複数のスレッドが並行的に同じ静的ローカル変数の初期化を試みた場合、初期化はちょうど一度だけ発生します (似た動作が std::call_once を用いて任意の関数に対して得ることができます)。 ノート: この機能の通常の実装はダブルチェックロッキングパターンの変種を使用します。 これはすでに初期化されたローカル静的変数に対する実行時のオーバーヘッドを単一の非アトミックなブーリアンの比較に削減します。 |
(C++11以上) |
ブロックスコープの静的変数に対するデストラクタはプログラムの終了時に呼ばれますが、初期化が行われ成功した場合に限ります。
同じインライン関数 (暗黙のインラインを含みます) のすべての定義内の関数ローカルな静的オブジェクトはすべて、ひとつの翻訳単位内で定義された同じオブジェクトを参照します。
[編集] ノート
const
であり extern
でないトップレベル名前空間スコープ (C ではファイルスコープ) の名前は、 C では外部リンケージを持ちますが、 C++ では内部リンケージを持ちます。
C++11 以降、 auto
は記憶域クラス指定子ではなくなりました。 auto
は型推定を表すために使用されます。
C では |
(C++17未満) |
C++ では、 C と異なり、変数は |
(C++17以上) |
異なるスコープから参照される内部または外部リンケージを持つ thread_local
変数の名前は、コードが同じスレッドで実行されるか別のスレッドで実行されるかによって、同じ実体または異なる実体を参照します。
extern
キーワードは言語リンケージおよびテンプレートの明示的実体化の宣言を指定するために使用することもできますが、それらのケースではそれは記憶域クラス指定子ではありません (ただし言語リンケージ指定に宣言が直接含まれるときは除きます。 その場合、宣言は extern
指定子を含むかのように扱われます)。
キーワード mutable
は、記憶域期間やリンケージに影響しませんが、 C++ の言語の文法上は、記憶域クラス指定子です。
This section is incomplete Reason: the rules about re-declaring names in the same TU |
記憶域クラス指定子は、 thread_local
を除き、明示的特殊化および明示的実体化では使用できません。
template <class T> struct S { thread_local static int tlm; }; template <> thread_local int S<float>::tlm = 0; // ここでは「static」は使用しません。
[編集] キーワード
auto, register, static, extern, thread_local, mutable
[編集] 例
#include <iostream> #include <string> #include <thread> #include <mutex> thread_local unsigned int rage = 1; std::mutex cout_mutex; void increase_rage(const std::string& thread_name) { ++rage; // ロックの外側で変更しても大丈夫です。 これはスレッドローカル変数です。 std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; } int main() { std::thread a(increase_rage, "a"), b(increase_rage, "b"); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for main: " << rage << '\n'; } a.join(); b.join(); }
出力例:
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 2387 | C++14 | unclear whether const-qualified variable template have internal linkage by default |
const qualifier does not affect the linkage of variable templates or their instances |
[編集] 関連項目
記憶域期間 の C言語リファレンス
|