非静的データメンバ
非静的データメンバはクラスのメンバ仕様における宣言です。
class S { int n; // 非静的データメンバ。 int& r; // 参照型の非静的データメンバ。 int a[2] = {1, 2}; // デフォルトメンバ初期化子付きの非静的データメンバ (C++11)。 std::string s, *ps; // 2個の非静的データメンバ。 struct NestedS { std::string s; } d5; // ネストした型の非静的データメンバ。 char bit : 2; // 2ビットのビットフィールド。 };
任意の単純宣言が使用できます。 ただし、
- extern および register 記憶域クラス指定子は使用できません。
- thread_local 記憶域クラス指定子は使用できません (静的データメンバに対しては使用できます)。
- 不完全型、抽象クラス型、およびそれらの配列は使用できません。 特に、クラス
C
はC
型の非静的データメンバを持つことはできません (C&
(C への参照) 型またはC*
(C へのポインタ) 型の非静的データメンバを持つことはできます)。 - ユーザ宣言されたコンストラクタが少なくともひとつ存在する場合、非静的データメンバはクラスの名前と同じ名前を持つことはできません。
- auto 指定子は非静的データメンバの宣言では使用できません (クラス定義内で初期化される静的データメンバに対しては使用できます)。
さらに、ビットフィールドの宣言が使用できます。
目次 |
[編集] レイアウト
何らかのクラス C
のオブジェクトが作成されるとき、非参照型の非静的データメンバはそれぞれ C
のオブジェクト表現のどこかの部分に確保されます。 参照メンバが何らかの記憶域を占めるかどうかは処理系定義ですが、記憶域期間はそのメンバが所属するオブジェクトのものと同じです。
非共用体クラス型の場合、同じメンバアクセスかつ非ゼロのサイズ (C++20以上)を持つメンバは常に、後に宣言されたメンバがクラスオブジェクト内でより高位のアドレスを持つように確保されます。 異なるアクセス制御を持つメンバは未規定の順序で確保されます (コンパイラはそれらを一緒にグループ化するかもしれません)。 アライメント要件によりメンバ間やクラスの最後のメンバの後にパディングが必要となる場合があります。
[編集] 標準レイアウト
すべての非静的データメンバが同じアクセス制御を持ち、その他の一定の条件を満たすクラスは、標準レイアウト型と言います (要件の一覧については StandardLayoutType を参照してください)。
ひとつ以上の先頭のメンバの並び (宣言順) について、それらのメンバがレイアウト互換な型である場合かつそれらがすべて [[no_unique_address]]
属性付きで宣言されたかすべてその属性なしで宣言された場合 (C++20以上)かつそれらがビットフィールドの場合は同じ幅を持つ場合 (C++14以上)、2つの標準レイアウト非共用体クラス型は非静的データメンバおよびビットフィールド (C++14以上)の共通先頭列を持つことがあります。
struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; // A と B の共通先頭列は A.a, A.b と B.b1, B.b2 です。 struct C { int c; unsigned : 0; char b; }; // A と C の共通先頭列は A.a と C.c です。 struct D { int d; char b : 4; }; // A と D の共通先頭列は A.a と D.d です。 struct E { unsigned int e; char b; }; // A と E の共通先頭列は空です。
2つの標準レイアウト非共用体クラス型が、同じ型である (cv 修飾は無視します) (C++14以上)、レイアウト互換な列挙 (すなわち同じ型をベースとする列挙) である、またはそれらの共通先頭列がすべての非静的データメンバおよびビットフィールド (C++14以上)から構成される場合、それらはレイアウト互換であると言います (上の例では、 A
と B
はレイアウト互換です)。
2つの標準レイアウト共用体が、同じ数の非静的データメンバを持ち、対応する非静的データメンバ (順不同) がレイアウト互換な型である場合、それらはレイアウト互換であると言います。
標準レイアウト型は以下の特別な性質を持ちます。
|
(C++14未満) |
|
(C++14以上) |
- 標準レイアウトクラス型のオブジェクトへのポインタは、その最初の非静的非ビットフィールドデータメンバ (非静的データメンバを持つ場合) またはそうでなければその基底クラス部分オブジェクトのいずれか (もしあれば) へのポインタに、またはその逆に、 reinterpret_cast することができます。 別の言い方をすると、標準レイアウト型の最初のデータメンバの前にはパディングが許されません。 そのようなキャストの結果には厳密なエイリアシングのルールが未だ適用されることに注意してください。
- マクロ offsetof は標準レイアウトクラスの先頭から任意のメンバへのオフセットを決定するために使用することができます。
[編集] メンバの初期化
非静的データメンバは2つの方法のいずれかで初期化できます。
struct S { int n; std::string s; S() : n(7) // n は直接初期化され、 s はデフォルト初期化されます。 { } };
2) デフォルトメンバ初期化子を通して。 これはメンバの宣言に含まれる波括弧または等号の初期化子であり、そのメンバがコンストラクタのメンバ初期化子リストに含まれていない場合に使用されます。
struct S { int n = 7; std::string s{'a', 'b', 'c'}; S() // デフォルトメンバ初期化子により、 n はコピー初期化され、 s はリスト初期化されます。 { } }; メンバがデフォルトメンバ初期化子を持ち、コンストラクタのメンバ初期化子リストにも現れる場合、そのコンストラクタに対してはデフォルトメンバ初期化子は無視されます。 Run this code #include <iostream> int x = 0; struct S { int n = ++x; S() { } // デフォルトメンバ初期化子を使用します。 S(int arg) : n(arg) { } // メンバ初期化子リストを使用します。 }; int main() { std::cout << x << '\n'; // 0 を表示します。 S s1; std::cout << x << '\n'; // 1 を表示します (デフォルト初期化子が実行されました)。 S s2(7); std::cout << x << '\n'; // 1 を表示します (デフォルト初期化子は実行されませんでした)。 } 出力: 0 1 1
配列型のメンバのサイズをメンバ初期化子から推定することはできません。 struct X { int a[] = {1,2,3}; // エラー。 int b[3] = {1,2,3}; // OK。 }; デフォルトメンバ初期化子は囲っているクラスに対するデフォルト化されたデフォルトコンストラクタの暗黙の定義またはそのコンストラクタの例外指定の原因となってはなりません。 : struct node { node* p = new node; // エラー、暗黙のまたはデフォルト化された node::node() の使用。 }; |
(C++11以上) | ||
参照メンバをデフォルトメンバ初期化子で一時オブジェクトに束縛することはできません (注: メンバ初期化子リストに対しても同じルールが存在します)。 struct A { A() = default; // OK。 A(int v) : v(v) { } // OK。 const int& v = 42; // OK。 }; A a1; // エラー、一時オブジェクトの参照への ill-formed な束縛 A a2(1); // OK (コンストラクタ内で v が初期化されているためデフォルトメンバ初期化子は無視されます) // ですが、 a2.v はダングリング参照です。 参照メンバは、同じ初期化子を使用する集成体初期化を実行する部分式を持つことになる場合、デフォルトメンバ初期化子では初期化できません。 struct A; extern A a; struct A { const A& a1{ A{a, a} }; // OK。 const A& a2{ A{} }; // エラー。 }; A a{a, a}; // OK。 |
(C++14以上) |
[編集] 使用方法
非静的データメンバまたは非静的メンバ関数の名前は、以下の3つの状況でのみ現れることができます。
this->
のメンバアクセス式を含みます。
struct S { int m; int n; int x = m; // OK、デフォルトメンバ初期化子で暗黙の this-> が使用できます (C++11)。 S(int i) : m(i), n(m) // OK、メンバ初期化子リストで暗黙の this-> が使用できます。 { this->f(); // 明示的なメンバアクセス式。 f(); // メンバ関数本体内で暗黙の this-> が使用できます。 } void f(); };
struct S { int m; void f(); }; int S::*p = &S::m; // OK、メンバへのポインタを作成するための m の使用。 void (S::*fp)() = &S::f; // OK、メンバへのポインタを作成するための f の使用。
3) 未評価被演算子内で使用されるとき (データメンバに対してのみです。 メンバ関数には適用されません)。
struct S { int m; static const std::size_t sz = sizeof m; // OK、 m は未評価被演算子です。 }; std::size_t j = sizeof(S::m + 42); // OK、たとえここでは m のための「this」オブジェクトがなくても。 |
(C++03以上) |
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 613 | C++03 | unevaluated uses of non-static data members not allowed | such uses are allowed |
CWG 1696 | C++14 | reference members could be initialized to temporaries (whose lifetime would end at the end of ctor) | such init is ill-formed |
CWG 1397 | C++11 | class was regarded as complete in the default member initializers | default member init cannot trigger definition of default ctor |
CWG 1719 | C++14 | differently cv-qualified types weren't layout-compatible | cv-quals ignored, spec improved |
CWG 2254 | C++14 | pointer to standard-layout class with no data members can be reinterpret_cast to its first base class | can be reinterpret_cast to any of its base classes |