Namespaces
Variants

Non-static data members

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

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ビットのビットフィールド
};

任意の 単純宣言 が許可されます。ただし以下を除く:

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

さらに、 bit-field の宣言も許可されています。

目次

レイアウト

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

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

(C++23まで)

union ではないクラス型の場合、 サイズがゼロではない メンバーは常に後から宣言されたメンバーがクラスオブジェクト内でより高いアドレスを持つように配置される。メンバーのアクセス制御は標準レイアウト特性に影響を与えることに注意(下記参照)。

(C++23以降)

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

スタンダードレイアウト

クラスは、それが PODクラス である場合に限り、 standard-layout であると見なされ、以下に記述される特性を持つ。

(C++11まで)

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

(C++11以降)

common initial sequence は、二つの標準レイアウト非共用体クラス型の最長の非静的データメンバおよびビットフィールドのシーケンスであり、各クラスの最初のそのようなエンティティから開始され、以下の条件を満たす宣言順のシーケンスである:

  • __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と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の共通先頭シーケンスは空

二つの標準レイアウト非共用体クラス型は、CV修飾子を無視した場合に同じ型であるか、レイアウト互換の 列挙型 (すなわち同じ基底型を持つ列挙型)である場合、またはそれらの 共通先頭シーケンス がすべての非静的データメンバーとビットフィールドから構成される場合、 レイアウト互換 であると呼ばれます(上記の例では、 A B はレイアウト互換です)。

二つの標準レイアウト共用体は、非静的データメンバーの数が同じで、対応する非静的データメンバー(任意の順序)がレイアウト互換型を持つ場合、 レイアウト互換 であると呼ばれます。

標準レイアウト型は以下の特別な特性を持ちます:

  • 標準レイアウト共用体において、非共用体クラス型 T1 のアクティブメンバーがある場合、別の非共用体クラス型 T2 の共用体メンバーの非静的データメンバー m を読み取ることが許可される。ただし、 m T1 T2 の共通先頭シーケンスの一部である場合に限る(ただし、volatileメンバーを非volatile glvalueを通じて読み取ることは未定義動作である)。
  • 標準レイアウトクラス型のオブジェクトへのポインタは、その最初の非静的・非ビットフィールドデータメンバー(非静的データメンバーがある場合)、または基底クラスのサブオブジェクト(基底クラスがある場合)へのポインタに reinterpret_cast することができ、逆も同様である。つまり、標準レイアウト型の最初のデータメンバーの前にパディングを置くことは許されない。なお、 strict aliasing 規則はこのようなキャストの結果に対しても適用される。
  • マクロ offsetof を使用して、標準レイアウトクラスの先頭から任意のメンバーまでのオフセットを決定することができる。

メンバー初期化

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

1) コンストラクタの メンバ初期化リスト において。
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をリスト初期化する
};

メンバがデフォルトメンバ初期化子を持ち、かつコンストラクタのメンバ初期化リストにも現れる場合、そのコンストラクタに対してデフォルトメンバ初期化子は無視される。

#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を出力
}

ビットフィールド メンバに対してはデフォルトメンバ初期化子は許可されない。

(C++20まで)

配列型のメンバは、メンバ初期化子からそのサイズを推論できない:

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

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

struct node
{
    node* p = new node; // エラー:暗黙的またはデフォルト化されたnode::node()の使用
};

参照メンバは、デフォルトメンバ初期化子内の一時オブジェクトに束縛できない(注:同じ規則が メンバ初期化子リスト にも存在する):

struct A
{
    A() = default;     // OK
    A(int v) : v(v) {} // OK
    const int& v = 42; // OK
};
A a1;    // エラー:参照への一時オブジェクトの不正な束縛
A a2(1); // OK(vがコンストラクタに現れるためデフォルトメンバ初期化子は無視される)
         // しかしa2.vはダングリング参照である
(C++11以降)


参照メンバーがそのデフォルトメンバー初期化子から初期化される場合 (C++20まで) メンバーがデフォルトメンバー初期化子を持つ場合 (C++20以降) であり、その potentially-evaluated 部分式が aggregate initialization であり、そのデフォルトメンバー初期化子を使用する場合、プログラムは不適格となります:

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以降)

使用方法

非静的データメンバーまたは非静的メンバー関数の名前が現れることができるのは、以下の三つの状況のみです:

1) クラスメンバアクセス式の一部として、そのクラスがこのメンバを持つか、このメンバを持つクラスから派生している場合、以下のコンテキストで非静的メンバ名が使用されるときに現れる暗黙の this - > メンバアクセス式を含む(メンバ関数本体内部、メンバ初期化子リスト内、クラス内デフォルトメンバ初期化子内で 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();
};
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 レイアウト互換性の決定時に、
すべてのメンバーが考慮されていた
非静的データメンバーのみを
考慮する
CWG 613 C++98 非静的データメンバーの未評価使用が許可されていなかった そのような使用が許可される
CWG 645 C++98 ビットフィールドと非ビットフィールドメンバーが
レイアウト互換かどうかが規定されていなかった
レイアウト互換ではない
CWG 1397 C++11 デフォルトメンバー初期化子内でクラスが
完全型と見なされていた
デフォルトメンバー初期化はデフォルトコンストラクタの
定義をトリガーできない
CWG 1425 C++98 標準レイアウトオブジェクトが最初の非静的データメンバー
または最初の基底クラス部分オブジェクトと同じアドレスを
共有するかどうかが不明確だった
非静的データメンバーが存在する場合はそれ、
そうでない場合は基底クラス部分オブジェクトが存在する場合はそれ
CWG 1696 C++98 参照メンバーが一時オブジェクトに初期化できた
(その寿命はコンストラクタの終了で終了する)
そのような初期化は不適格
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 ]] で宣言された
メンバーを含む可能性があった
それらは含まれない

関連項目

クラス
静的メンバ
非静的メンバ関数
型が standard-layout 型かどうかをチェックする
(クラステンプレート)
standard-layout 型の先頭から指定されたメンバへのバイトオフセット
(関数マクロ)