Storage class specifiers
ストレージクラス指定子は、名前の decl-specifier-seq の一部です。 declaration syntax において、名前の scope と共に、名前の2つの独立した特性:その storage duration と linkage を制御します。
目次 |
ストレージ期間
storage duration は、 object が格納されているストレージの最小潜在的な寿命を定義するオブジェクトの特性です。ストレージ期間は、オブジェクトを作成するために使用された構文によって決定され、以下のいずれかとなります:
- staticストレージ期間
|
(C++11以降) |
- 自動ストレージ期間
- 動的ストレージ期間
Static 、thread、 (C++11以降) および自動ストレージ期間は、 宣言 によって導入されるオブジェクトおよび 一時オブジェクト に関連付けられます。動的ストレージ期間は、 new 式 によって作成されるオブジェクト、または 暗黙的に作成されるオブジェクト に関連付けられます。
ストレージ期間のカテゴリは参照にも適用されます。
サブオブジェクトと参照メンバのストレージ期間は、それらの完全オブジェクトのストレージ期間と同じです。
指定子
以下のキーワードは storage class specifiers です:
|
(C++11まで) |
|
(C++17まで) |
- static
|
(C++11以降) |
- extern
- mutable
decl-specifier-seq において、最大で1つのストレージクラス指定子を指定できます 、ただし thread_local は static または extern と共に使用できます (C++11以降) 。
mutable はストレージ期間に影響を与えません。使用方法については、 const/volatile を参照してください。
他のストレージクラス指定子は、以下の宣言の decl-specifier-seq に現れることができます:
| 指定子 | 以下の decl-specifier-seq に現れる可能性 | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 変数宣言 | 関数宣言 |
構造化束縛宣言
(C++17以降) |
|||||||
| 非メンバー | メンバー | 非メンバー | メンバー | ||||||
| 非パラメータ | 関数パラメータ | 非静的 | 静的 | 非静的 | 静的 | ||||
| auto | ブロックスコープのみ | 可 | 不可 | 不可 | 不可 | 不可 | 不可 | N/A | |
| register | ブロックスコープのみ | 可 | 不可 | 不可 | 不可 | 不可 | 不可 | N/A | |
| static | 可 | 不可 | 静的を宣言 | 名前空間スコープのみ | 静的を宣言 | 可 | |||
| thread_local | 可 | 不可 | 不可 | 可 | 不可 | 不可 | 不可 | 可 | |
| extern | 可 | 不可 | 不可 | 不可 | 可 | 不可 | 不可 | 不可 | |
匿名共用体 は static で宣言することもできます。
|
register は、宣言された変数が頻繁に使用されるため、その値をCPUレジスタに格納できることを示すヒントです。このヒントは無視されることがあり、ほとんどの実装では変数のアドレスが取得されると無視されます。この使用方法は非推奨です。 |
(C++17まで) |
静的ストレージ期間
以下のすべての条件を満たす変数は static storage duration を持ちます:
- これは 名前空間スコープ に属するか、または最初に static または extern で宣言される。
|
(since C++11) |
これらのエンティティのストレージはプログラムの存続期間中保持されます。
スレッドストレージ期間thread_local で宣言されたすべての変数は スレッドストレージ期間 を持ちます。 これらのエンティティのストレージは、それらが作成されたスレッドの存続期間中続きます。スレッドごとに個別のオブジェクトまたは参照が存在し、宣言された名前を使用すると現在のスレッドに関連付けられたエンティティを参照します。 |
(C++11以降) |
自動ストレージ期間
以下の変数は automatic storage duration を持ちます:
- ブロックスコープに属し、明示的に static 、 thread_local 、 (C++11以降) または extern として宣言されていない変数。このような変数のストレージは、それらが作成されたブロックが終了するまで存続します。
- パラメータスコープ(すなわち関数パラメータ)に属する変数。関数パラメータのストレージは、その 破棄 直後まで存続します。
動的ストレージ期間
プログラム実行中に以下の方法で作成されたオブジェクトは dynamic storage duration を持ちます:
- new 式 。このようなオブジェクトのストレージは アロケーション関数 によって割り当てられ、 デアロケーション関数 によって解放される。
- 暗黙的な生成 (他の手段による)。このようなオブジェクトのストレージは既存のストレージと重複する。
- 例外オブジェクト 。このようなオブジェクトのストレージは未指定の方法で割り当てられ解放される。
リンケージ
名前は external linkage , module linkage (since C++20) , internal linkage 、または no linkage を持つことができます:
- 外部リンケージを持つ名前のエンティティは、 再宣言 することができ、その再宣言は別の 翻訳単位 で行うことができます 。さらに、その再宣言は 異なるモジュールに付属 させることができます (C++20以降) 。
|
(C++20以降) |
- 内部リンケージを持つ名前のエンティティは、同じ翻訳単位内の別のスコープで再宣言できます。
- リンケージを持たない名前のエンティティは、同じスコープ内でのみ再宣言できます。
以下のリンケージが認識されます:
リンケージなし
ブロックスコープで宣言された以下のいずれかの名前はリンケージを持ちません:
- 明示的に宣言されていない変数 extern ( static 修飾子の有無に関わらず);
- ローカルクラス とそのメンバー関数;
- ブロックスコープで宣言されるその他の名前(typedef、列挙型、列挙子など)。
externalで指定されていない名前は、 , module, (C++20以降) またはinternal linkageも持たず、それらが宣言されたスコープに関わらずlinkageを持ちません。
内部リンケージ
名前空間スコープで宣言された以下の名前のいずれかは内部リンケージを持ちます:
- variables , variable templates (since C++14) , functions, or function templates declared static ;
- non-template (since C++14) variables of non-volatile const-qualified type, unless
|
(C++17以降) |
|
(C++20以降) |
-
- それらが明示的に extern として宣言されている場合、または
- 以前に宣言されており、その前の宣言が内部リンケージを持たなかった場合
- 匿名共用体 のデータメンバ。
|
さらに、 無名名前空間 内、または無名名前空間内の名前空間で宣言されたすべての名前は、 extern を明示的に指定して宣言されたものであっても、内部リンケージを持ちます。 |
(C++11以降) |
外部リンケージ
外部リンケージを持つ変数と関数は 言語リンケージ も持ち、異なるプログラミング言語で書かれた翻訳単位をリンクすることが可能になります。
名前空間スコープで宣言された以下のいずれかの名前は、無名名前空間で宣言されている場合を除き外部リンケージを持ちます またはその宣言が名前付きモジュールに付属しエクスポートされていない場合を除きます (C++20以降) :
- 上記にリストされていない変数および関数(すなわち、 static として宣言されていない関数、 static として宣言されていない非const変数、および extern として宣言されたすべての変数);
- 列挙型;
- クラス名、それらのメンバー関数、静的データメンバー(constか否かを問わない)、ネストされたクラスと列挙型、およびクラス本体内部で friend 宣言で初めて導入された関数の名前;
- 上記にリストされていないすべてのテンプレートの名前(すなわち、 static として宣言されていない関数テンプレート以外);
以下の名前のいずれかがブロックスコープで最初に宣言された場合、外部リンケージを持ちます:
- externで宣言された変数名 extern ;
- 関数名
モジュールリンケージ名前空間スコープで宣言された名前は、その宣言が名前付きモジュールに付属し、エクスポートされておらず、内部リンケージを持たない場合、モジュールリンケージを持ちます。 |
(C++20以降) |
|
この節は不完全です
理由: 同じ翻訳単位で異なるリンケージを持つエンティティが宣言された場合の動作の説明を追加(6.6 パラグラフ 6)、C++20(不適格)と現在のドラフト(適格)の違いに注意 |
静的ブロック変数
静的ストレージ期間 またはスレッド (C++11以降) を持つブロック変数は、制御がその宣言を最初に通過する際に初期化されます(ただし、その初期化が ゼロ初期化 または 定数初期化 である場合は除きます。これらはブロックが最初に進入される前に行うことができます)。それ以降のすべての呼び出しでは、宣言はスキップされます。
- 初期化が 例外を送出する 場合、変数は初期化済みとは見なされず、次に制御がその宣言を通過する際に再度初期化が試行されます。
- 初期化が変数が初期化されているブロックに再帰的に入る場合、動作は未定義です。
|
(C++11以降) |
静的記憶域期間を持つブロック変数のデストラクタは プログラム終了時に呼び出されます 。ただし、初期化が正常に行われた場合に限ります。
外部リンケージを持つ関数(暗黙的にインラインである場合を含む)において、同じ インライン関数 のすべての定義内で静的記憶域期間を持つ変数は、1つの翻訳単位で定義された同じオブジェクトを参照します。
翻訳単位ローカルエンティティ
translation-unit-localエンティティの概念はC++20で標準化されました。詳細は このページ をご覧ください。
エンティティは、以下の場合に translation-unit-local (または略して TU-local )となります:
- 内部リンケージを持つ名前を持つ、または
- リンケージを持つ名前を持たず、TUローカルなエンティティの定義内で導入される、または
- テンプレート引数またはテンプレート宣言がTUローカルなエンティティを使用するテンプレートまたはテンプレート特殊化である。
TU-localエンティティに依存する非TU-localエンティティの型がある場合、または非TU-localエンティティの宣言 、あるいはその 導出ガイド が、 (C++17以降) その外部でTU-localエンティティを参照する場合、悪いこと(通常は ODR 違反)が発生する可能性があります。
- 非インライン関数または関数テンプレートの function-body
- 変数または変数テンプレートの initializer
- クラス定義内の friend declarations
- 変数の値の使用(その変数が constant expressions で使用可能な場合
|
このような使用は、 モジュールインターフェース単位 (そのプライベートモジュールフラグメントがある場合はそれを除く)またはモジュールパーティションでは許可されず、その他の文脈では非推奨です。 1つの翻訳単位に現れる宣言は、ヘッダーユニットではない別の翻訳単位で宣言されたTU-localエンティティを名前付けできません。 テンプレート に対してインスタンス化された宣言は、特殊化のインスタンス化ポイントに現れます。 |
(C++20以降) |
注記
トップレベルの名前空間スコープ(C言語ではファイルスコープ)にある名前で、 const が付き、 extern が付いていないものは、C言語では外部リンケージを持ちますが、C++では内部リンケージを持ちます。
C++11以降、 auto はストレージクラス指定子ではなく、型推論を示すために使用されます。
|
Cでは、 register 変数のアドレスを取得することはできないが、C++では、 register を指定して宣言された変数は、ストレージクラス指定子なしで宣言された変数と意味的に区別できない。 |
(C++17まで) |
|
C++では、Cとは異なり、変数を register として宣言することはできない。 |
(C++17以降) |
内部リンケージまたは外部リンケージを持つ thread_local 変数の名前は、同じスレッドまたは異なるスレッドでコードが実行されているかどうかに応じて、同じインスタンスまたは異なるインスタンスを参照する可能性があります。
extern キーワードは、 言語リンケージ および 明示的なテンプレートインスタンス化宣言 を指定するためにも使用できますが、これらのケースではストレージクラス指定子ではありません(ただし、宣言が言語リンケージ指定内に直接含まれている場合は除きます。その場合、宣言は extern 指定子を含むものとして扱われます)。
thread_local を除くストレージクラス指定子は、 明示的特殊化 および 明示的インスタンス化 では許可されません:
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // ここには"static"は現れない
|
const ( constexpr によって暗黙的に指定される場合もある)変数テンプレートは、従来デフォルトで内部リンケージを持っていましたが、これは他のテンプレート化されたエンティティと一貫性がありませんでした。欠陥報告 CWG2387 によってこれは修正されました。 |
(C++14以降) |
inline
は
CWG2387
に対する回避策として、デフォルトで外部リンケージを与えるように機能します。これが、多くの変数テンプレートに
inline
が
追加され
、CWG2387が受理された後に
削除された
理由です。サポートされているコンパイラがCWG2387を実装していない限り、標準ライブラリの実装も
inline
を使用する必要があります。
GCC Bugzilla #109126
および
MSVC STL PR #4546
を参照してください。
|
(C++17以降) |
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_threadsafe_static_init
|
200806L
|
(C++11) | 並行性を伴う動的初期化と破棄 |
キーワード
auto , register , static , extern , thread_local , mutable
例
#include <iostream> #include <mutex> #include <string> #include <thread> 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 216 | C++98 |
クラススコープ内の無名クラスおよび列挙型は
名前空間スコープ内のものとは異なるリンケージを持つ |
これらのスコープではすべて外部
リンケージを持つ |
| CWG 389 | C++98 |
リンケージを持たない名前は
リンケージを持つエンティティの宣言に使用できない |
リンケージを持たない型は
リンケージを持つ変数または関数の型として 使用してはならない。ただし、その変数または関数が C言語リンケージを持つ場合は除く |
| CWG 426 | C++98 |
エンティティが同じ翻訳単位内で内部リンケージと
外部リンケージの両方で宣言される可能性があった |
この場合、プログラムは不適格 |
| CWG 527 | C++98 |
CWG 389の解決によって導入された型制限は、
自身の翻訳単位外で名前を参照できない変数と関数にも 適用されていた |
これらの変数と関数に対して制限は解除される
(すなわち、リンケージを持たないか内部リンケージを持つ、 または無名名前空間内で宣言されたもの) |
| CWG 809 | C++98 | register はほとんど機能していなかった | 非推奨 |
| CWG 1648 | C++11 |
static
は
thread_local が extern と組み合わされた場合でも暗黙的に指定された |
他のストレージクラス指定子が
存在しない場合にのみ暗黙的に指定される |
| CWG 1686 |
C++98
C++11 |
名前空間スコープで宣言された非static変数の名前は、
明示的に const (C++98)または constexpr (C++11)と 宣言された場合にのみ内部リンケージを持っていた |
型がconst修飾されていることのみを要求 |
| CWG 2019 | C++98 |
参照メンバのストレージ期間は
規定されていなかった |
完全オブジェクトと同じ |
| CWG 2387 | C++14 |
const修飾された変数テンプレートがデフォルトで
内部リンケージを持つかどうかが不明確 |
const修飾子は変数テンプレートまたは
そのインスタンスのリンケージに 影響しない |
| CWG 2533 | C++98 |
暗黙的に作成されたオブジェクトの
ストレージ期間が不明確だった |
明確化された |
| CWG 2850 | C++98 |
関数パラメータのストレージが
いつ解放されるか不明確だった |
明確化された |
| CWG 2872 | C++98 | 「参照できる」の意味が不明確だった | 文言を改善 |
| P2788R0 | C++20 |
名前空間でconst修飾された変数を宣言すると、
モジュールユニット内でも内部リンケージが与えられた |
内部リンケージは与えられない |
参考文献
- C++23標準 (ISO/IEC 14882:2024):
-
- 6.7.5 記憶域期間 [basic.stc]
- C++20標準 (ISO/IEC 14882:2020):
-
- 6.7.5 記憶域期間 [basic.stc]
- C++17標準 (ISO/IEC 14882:2017):
-
- 6.7 記憶域期間 [basic.stc]
- C++14標準 (ISO/IEC 14882:2014):
-
- 3.7 記憶域期間 [basic.stc]
- C++11標準 (ISO/IEC 14882:2011):
-
- 3.7 記憶域期間 [basic.stc]
- C++03規格 (ISO/IEC 14882:2003):
-
- 3.7 記憶域期間 [basic.stc]
- C++98標準 (ISO/IEC 14882:1998):
-
- 3.7 記憶域期間 [basic.stc]
関連項目
|
C ドキュメント
for
ストレージ期間
|