名前空間
変種
操作

非静的データメンバー

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

非静的データメンバーは、クラスのメンバー仕様で宣言されます。

class S
{
    int n;              // non-static data member
    int& r;             // non-static data member of reference type
    int a[2] = {1, 2};  // non-static data member with default member initializer (C++11)
    std::string s, *ps; // two non-static data members
 
    struct NestedS
    {
        std::string s;
    } d5;               // non-static data member of nested type
 
    char bit : 2;       // two-bit bitfield
};

任意の単純宣言が許可されますが、例外があります。

  • extern および register ストレージクラス指定子は許可されません。
  • thread_local ストレージクラス指定子は許可されません(ただし、静的データメンバーには許可されます)。
(C++11以降)
  • 不完全型抽象クラス型、およびそれらの配列は許可されません。特に、クラスCは型Cの非静的データメンバーを持つことはできませんが、型C&(Cへの参照)またはC*(Cへのポインタ)の非静的データメンバーを持つことはできます。
  • 少なくとも1つのユーザー宣言コンストラクタが存在する場合、非静的データメンバーはクラス名と同じ名前を持つことはできません。
(C++11以降)

さらに、ビットフィールド宣言は許可されます。

目次

[編集] レイアウト

あるクラスCのオブジェクトが作成されるとき、参照型でない各非静的データメンバーは、Cのオブジェクト表現のいずれかの部分に割り当てられます。参照メンバーがストレージを占有するかどうかは実装定義ですが、それらのストレージ期間は、それらがメンバーであるオブジェクトのストレージ期間と同じです。

unionクラス型の場合、非ゼロサイズの(C++20以降)メンバーは、アクセス指定子で区切られていない(C++11まで)同じメンバーアクセスを持つ(C++11以降)、後で宣言されたメンバーがクラスオブジェクト内でより高いアドレスを持つように常に割り当てられます。メンバーはアクセス指定子で区切られている(C++11まで)異なるアクセス制御を持つ(C++11以降)、指定されていない順序で割り当てられます(コンパイラがそれらをグループ化する場合があります)。

(C++23まで)

unionクラス型の場合、非ゼロサイズのメンバーは、後で宣言されたメンバーがクラスオブジェクト内でより高いアドレスを持つように常に割り当てられます。メンバーのアクセス制御は、標準レイアウトプロパティ(以下参照)に引き続き影響することに注意してください。

(C++23から)

アライメント要件により、メンバー間、またはクラスの最後のメンバーの後にパディングが必要になる場合があります。

[編集] 標準レイアウト

クラスは、PODクラスである場合にのみ、標準レイアウトと見なされ、以下に説明するプロパティを持つとされます。

(C++11まで)

すべての非静的データメンバーが同じアクセス制御を持ち、特定の他の条件が満たされているクラスは、標準レイアウトクラスとして知られています(要件のリストについては、標準レイアウトクラスを参照してください)。

(C++11以降)

2つの標準レイアウト非unionクラス型の共通初期シーケンスは、宣言順序での非静的データメンバーとビットフィールドの最も長いシーケンスであり、各クラスの最初のエンティティから開始され、以下の条件を満たします。

  • もし __has_cpp_attribute(no_unique_address)0 でない場合、どちらのエンティティも [[no_unique_address]] 属性で宣言されていない。
(C++20以降)
  • 対応するエンティティはレイアウト互換な型を持つ。
  • 対応するエンティティは同じアライメント要件を持つ。
  • 両方のエンティティが同じ幅のビットフィールドであるか、またはどちらもビットフィールドでない。
struct A { int a; char b; };
struct B { const int b1; volatile char b2; }; 
// A and B's common initial sequence is A.a, A.b and B.b1, B.b2
 
struct C { int c; unsigned : 0; char b; };
// A and C's common initial sequence is A.a and C.c
 
struct D { int d; char b : 4; };
// A and D's common initial sequence is A.a and D.d
 
struct E { unsigned int e; char b; };
// A and E's common initial sequence is empty

2つの標準レイアウト非unionクラス型は、それらがcv-修飾子を無視すると同じ型である場合、列挙型(つまり、同じ基底型を持つ列挙型)とレイアウト互換である場合、またはそれらの共通初期シーケンスがすべての非静的データメンバーとビットフィールドで構成される場合(上記の例では、ABはレイアウト互換です)、レイアウト互換と呼ばれます。

2つの標準レイアウトunionは、非静的データメンバーの数が同じであり、対応する非静的データメンバー(順不同)がレイアウト互換な型を持つ場合、レイアウト互換と呼ばれます。

標準レイアウト型は以下の特別なプロパティを持ちます。

  • アクティブなメンバーが非unionクラス型T1である標準レイアウトunionにおいて、別のunionメンバーの非unionクラス型T2の非静的データメンバーmを読み取ることが許可されます。ただし、mT1T2の共通初期シーケンスの一部である場合(ただし、非volatile lvalueを通してvolatileメンバーを読み取ることは未定義です)。
  • 標準レイアウトクラス型のオブジェクトへのポインタは、その最初の非静的非ビットフィールドデータメンバー(非静的データメンバーがある場合)またはそうでない場合は任意の基底クラスサブオブジェクト(基底クラスがある場合)へのポインタにreinterpret_castでき、その逆も同様です。言い換えれば、標準レイアウト型の最初のデータメンバーの前にパディングは許可されません。このようなキャストの結果には、厳密なエイリアシング規則が引き続き適用されることに注意してください。
  • マクロ offsetof は、標準レイアウトクラスの先頭からの任意のメンバーのオフセットを決定するために使用できます。

[編集] メンバー初期化

非静的データメンバーは、次のいずれかの方法で初期化できます。

1) メンバー初期化子リストのコンストラクタ内。
struct S
{
    int n;
    std::string s;
    S() : n(7) {} // direct-initializes n, default-initializes s
};
2) デフォルトメンバー初期化子を介して。これは、メンバー宣言に含まれるブレースまたは等号初期化子であり、コンストラクタのメンバー初期化子リストからメンバーが省略された場合に使用されます。
struct S
{
    int n = 7;
    std::string s{'a', 'b', 'c'};
    S() {} // default member initializer will copy-initialize n, list-initialize s
};

メンバーがデフォルトメンバー初期化子を持ち、コンストラクタのメンバー初期化リストにも登場する場合、そのコンストラクタではデフォルトメンバー初期化子は無視されます。

#include <iostream>
 
int x = 0;
struct S
{
    int n = ++x;
    S() {}                 // uses default member initializer
    S(int arg) : n(arg) {} // uses member initializer 
};
 
int main()
{
    std::cout << x << '\n'; // prints 0
    S s1;                   // default initializer ran
    std::cout << x << '\n'; // prints 1
    S s2(7);                // default initializer did not run
    std::cout << x << '\n'; // prints 1
}

ビットフィールドメンバーには、デフォルトメンバー初期化子は許可されません。

(C++20まで)

配列型のメンバーは、メンバー初期化子からサイズを推論できません。

struct X
{
    int a[] = {1, 2, 3};  // error
    int b[3] = {1, 2, 3}; // OK
};

デフォルトメンバー初期化子は、囲むクラスのデフォルトコンストラクタの暗黙的な定義、またはそのコンストラクタの例外仕様を引き起こすことは許可されていません。

struct node
{
    node* p = new node; // error: use of implicit or defaulted node::node() 
};

参照メンバーは、デフォルトメンバー初期化子で一時オブジェクトにバインドすることはできません(注:メンバー初期化子リストにも同じ規則が存在します)。

struct A
{
    A() = default;     // OK
    A(int v) : v(v) {} // OK
    const int& v = 42; // OK
};
 
A a1;    // error: ill-formed binding of temporary to reference
A a2(1); // OK (default member initializer ignored because v appears in a constructor)
         // however a2.v is a dangling reference
(C++11以降)


もし 参照メンバーがデフォルトメンバー初期化子から初期化される(C++20まで)メンバーがデフォルトメンバー初期化子を持つ(C++20以降)、およびそのメンバーの潜在的に評価されるサブ式が、そのデフォルトメンバー初期化子を使用する集計初期化である場合、プログラムは ill-formed です。

struct A;
extern A a;
 
struct A
{
    const A& a1{A{a, a}}; // OK
    const A& a2{A{}};     // error
};
 
A a{a, a};                // OK
(C++17以降)

[編集] 使用法

非静的データメンバーまたは非静的メンバー関数の名前は、次の3つの状況でのみ出現できます。

1) クラスメンバーアクセス式の部分として。この場合、クラスはそのメンバーを持つか、またはそのメンバーを持つクラスから派生しています。これには、非静的メンバー名がthisが許可されるコンテキスト(メンバー関数の本体内、メンバー初期化子リスト内、クラス内デフォルトメンバー初期化子内)のいずれかで使用されるときに現れる暗黙の this-> メンバーアクセス式が含まれます。
struct S
{
    int m;
    int n;
    int x = m;            // OK: implicit this-> allowed in default initializers (C++11)
 
    S(int i) : m(i), n(m) // OK: implicit this-> allowed in member initializer lists
    {
        this->f();        // explicit member access expression
        f();              // implicit this-> allowed in member function bodies
    }
 
    void f();
};
2) 非静的メンバーへのポインタを形成するため。
struct S
{
    int m;
    void f();
};
 
int S::*p = &S::m;       // OK: use of m to make a pointer to member
void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member
3) (データメンバーのみ、メンバー関数は除く)評価されないオペランドで使用される場合。
struct S
{
    int m;
    static const std::size_t sz = sizeof m; // OK: m in unevaluated operand
};
 
std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
注:このような使用は、CWG issue 613 の解決を介して、N2253 にて許可されており、一部のコンパイラ(例:clang)ではC++11の変更と見なされます。

[編集] 注釈

機能テストマクロ 規格 機能
__cpp_nsdmi 200809L (C++11) 非静的データメンバー初期化子
__cpp_aggregate_nsdmi 201304L (C++14) 集計クラスデフォルトメンバー初期化子

[編集] 欠陥レポート

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 80 C++98 すべてのデータメンバーがクラス名と同じ名前を持つことはできません。
(C互換性に影響)
非静的データメンバーを許可する
クラス名と共有する、もし
ユーザー宣言コンストラクタがない場合。
CWG 190 C++98 レイアウト互換性を判断する際、
すべてのメンバーが考慮された。
非-を考慮する
静的データメンバ (static data members)
CWG 613 C++98 評価されない非静的データメンバーの使用は許可されなかった。 そのような使用は許可される。
CWG 645 C++98 ビットフィールドと
非ビットフィールドメンバーがレイアウト互換であるかどうかは不明確だった。
レイアウト互換ではない。
CWG 1397 C++11 クラスは完了と見なされた。
デフォルトメンバー初期化子で。
デフォルトメンバー初期化子はトリガーできない。
デフォルトコンストラクタの定義。
CWG 1425 C++98 標準レイアウトオブジェクトが
最初の非静的データメンバーまたは
最初の基底クラスサブオブジェクトと同じアドレスを共有するかどうかは不明確だった。
非静的データメンバー。
存在する場合、そうでなければ基底。
クラスサブオブジェクトが存在する場合。
CWG 1696 C++98 参照メンバーが一時オブジェクトに初期化される可能性があった。
(その寿命はコンストラクタの終了時に終了する)
そのような初期化はill-formed。
CWG 1719 C++98 異なるcv-修飾された型はレイアウト互換ではなかった。 cv-修飾子は無視され、仕様は改善された。
CWG 2254 C++11 データメンバーを持たない標準レイアウトクラスへのポインタは
最初の基底クラスへのreinterpret_castが可能。
reinterpret_castが可能。
任意の基底クラスへの。
CWG 2583 C++11 共通初期シーケンスは
アライメント要件を考慮しなかった。
考慮された
CWG 2759 C++20 共通初期シーケンスには含めることができた。
[[no_unique_address]] で宣言されたメンバー。
それらは含まれない。

[編集] 関連項目

クラス
静的メンバー
非静的メンバー関数
型がスタンダードレイアウト型であるかをチェックする
(クラステンプレート) [編集]
標準レイアウト型の先頭から指定されたメンバまでのバイトオフセット
(関数マクロ) [編集]
English 日本語 中文(简体) 中文(繁體)