cv (const および volatile) 型修飾子
宣言されているオブジェクトまたは表されている型の定数性や揮発性を指定するために、型指定子 (宣言の文法の decl-specifier-seq を含む) 内に現れます。
const
ー 型が定数であると定義します。volatile
ー 型が揮発であると定義します。
目次 |
[編集] 説明
関数型または参照型以外の任意の型 T
(不完全型を含む) について、さらに3つの異なる型 (const 修飾された T
、 volatile 修飾された T
、 const volatile 修飾された T
) が C++ の型システムに存在します。
- ノート: 配列型はその要素の型と同じ cv 修飾を持つとみなされます。
オブジェクトが最初に作成されたとき、使用された cv 修飾 (宣言の decl-specifier-seq または declarator の一部、または new 式の type-id の一部として指定できます) は、以下のように、そのオブジェクトの定数性や揮発性を決定します。
- const オブジェクト ー const 修飾された型のオブジェクト、または const オブジェクトの mutable でない部分オブジェクト。 そのようなオブジェクトは変更できません。 直接変更する試みはコンパイル時エ���ーであり、間接的に変更する (例えば非 const 型への参照やポインタを通して const オブジェクトを変更する) 試みは未定義動作です。
- volatile オブジェクト ー volatile 修飾された型のオブジェクト、または volatile オブジェクトの部分オブジェクト、または const volatile オブジェクトの mutable な部分オブジェクト。 volatile 修飾された型の glvalue 式を通して行われたすべてのアクセス (読み書き操作、メンバ関数呼び出しなど) は、最適化の目的に対して可視な副作用として扱われます (つまり、単一の実行のスレッド内では、 volatile アクセスを最適化で除去したり、その volatile アクセスに対して先行配列または後続配列される別の可視な副作用と並び替えたりすることはできません。 これにより、 volatile オブジェクトはシグナルハンドラとの通信に適することとなりますが、別の実行のスレッドとの通信には適しません。 std::memory_order を参照してください)。 非 volatile な glvalue (非 volatile 型への参照やポインタなど) を通して volatile オブジェクトを参照する試みは未定義動作です。
- const volatile オブジェクト ー const volatile 修飾された型のオブジェクト、 const volatile オブジェクトの mutable でない部分オブジェクト、 volatile オブジェクトの const な部分オブジェクト、または const オブジェクトの mutable でない volatile な部分オブジェクト。 const オブジェクトと volatile オブジェクトの両方として振る舞います。
This section is incomplete Reason: should discuss more about the differences between cv-qualified objects and cv-qualified expressions |
[編集] mutable 指定子
mutable
ー たとえ含まれているオブジェクトが const 宣言されていても、 mutable 宣言されたクラスメンバの変更は許容します。
参照でなく const でない非静的クラスメンバの宣言に現れることができます。
class X { mutable const int* p; // OK mutable int* const q; // ill-formed };
mutable はメンバがクラスの外部から可視な状態に影響しないことを指定するために使用されます (ミューテックス、キャッシュ、遅延評価、およびアクセス計測に対してしばしば使用されます)。
class ThreadsafeCounter { mutable std::mutex m; // 「M&M ルール」: mutable と mutex は仲良しです。 int data = 0; public: int get() const { std::lock_guard<std::mutex> lk(m); return data; } void inc() { std::lock_guard<std::mutex> lk(m); ++data; } };
[編集] 変換
cv 修飾には制約が増える順序による半順序関係があります。 型は以下のようにより多くまたはより少なく cv 修飾されていると言うことができます。
- 無修飾 <
const
- 無修飾 <
volatile
- 無修飾 <
const volatile
-
const
<const volatile
-
volatile
<const volatile
- 無修飾 <
cv 修飾された型への参照またはポインタは、より多く cv 修飾されている型への参照またはポインタに暗黙に変換されます。 特に、以下の変換が許容されます。
- 無修飾な型への参照またはポインタは
const
な型への参照またはポインタに変換できます。 - 無修飾な型への参照またはポインタは
volatile
な型への参照またはポインタに変換できます。 - 無修飾な型への参照またはポインタは
const volatile
な型への参照やポインタに変換できます。 -
const
な型への参照またはポインタはconst volatile
な型への参照またはポインタに変換できます。 -
volatile
な型への参照またはポインタはconst volatile
な型への参照またはポインタに変換できます。
- 無修飾な型への参照またはポインタは
- ノート: 複数段のポインタには追加の制約が課されます。
cv 修飾された型への参照またはポインタをより少なく cv 修飾された型への参照またはポインタに変換するには、 const_cast を使用しなければなりません。
[編集] キーワード
[編集] ノート
extern
宣言されていない非ローカル、非 volatile、非テンプレート (C++14以上)、非インライン (C++17以上)な変数の宣言に使用される const
修飾子は、その変数に内部リンケージを与えます。 これは const なファイルスコープの変数が外部リンケージ持つ C とは異なります。
C++ の言語の文法では、 mutable
は型指定子ではなく記憶域クラス指定子として扱っていますが、記憶域クラスやリンケージには影響しません。
[編集] 例
int main() { int n1 = 0; // 非 const オブジェクト const int n2 = 0; // const オブジェクト int const n3 = 0; // const オブジェクト (n2 と同じ) volatile int n4 = 0; // volatile オブジェクト const struct { int n1; mutable int n2; } x = {0, 0}; // mutable メンバを持つ const オブジェクト n1 = 1; // OK、変更可能なオブジェクト // n2 = 2; // エラー、変更可能でないオブジェクト n4 = 3; // OK、副作用として扱われます // x.n1 = 4; // エラー、 const オブジェクトのメンバは const です x.n2 = 4; // OK、 const オブジェクトの mutable メンバは const ではありません const int& r1 = n1; // 非 const オブジェクトに束縛された const への参照 // r1 = 2; // エラー、 const への参照を通して変更しようとしています const_cast<int&>(r1) = 2; // OK、非 const オブジェクト n1 を変更します const int& r2 = n2; // const オブジェクトに束縛された const への参照 // r2 = 2; // エラー、 const への参照を通して変更しようとしています // const_cast<int&>(r2) = 2; // 未定義動作、 const オブジェクト n2 を変更しようとしています }
出力:
# x86_64 プラットフォーム上で生成される一般的な機械語コード # (観察可能な副作用に寄与するコードのみが生成されます) main: movl $0, -4(%rsp) # volatile int n4 = 0; movl $3, -4(%rsp) # n4 = 3; xorl %eax, %eax # return 0 (暗黙) ret
[編集] 関連項目
const 修飾子 の C言語リファレンス
| |
volatile 修飾子 の C言語リファレンス
|