Namespaces
Variants

Constructors and member initializer lists

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

コンストラクタ は、特別な宣言子構文で宣言される非静的 メンバー関数 であり、それらのクラス型のオブジェクトを初期化するために使用されます。

コンストラクタは コルーチン にすることはできません。

(C++20以降)

コンストラクタは 明示的なオブジェクトパラメータ を持つことはできません。

(C++23以降)
翻訳のポイント: - HTMLタグ、属性、
タグ内のテキストは翻訳せず、元のまま保持
- C++固有の用語(constructor, coroutine, explicit object parameter)は翻訳せず、技術文書としての正確性を維持
- 自然な日本語の技術文書としての表現を心がけ、専門性を保つ
- フォーマットと構造は完全に保持

目次

構文

コンストラクタは、以下の形式のメンバー function declarators を使用して宣言されます:

class-name ( parameter-list  (オプション) ) except  (オプション) attr  (オプション)
class-name - 識別子式、 属性のリストが続く可能性があり、そして (C++11以降) 括弧の組で囲まれる可能性がある
parameter-list - パラメータリスト
except -

動的例外指定

(C++11まで)

動的例外指定
または noexcept指定

(C++11以降)
(C++17まで)

noexcept指定

(C++17以降)
attr - (C++11以降) 属性のリスト

コンストラクタ宣言の 宣言指定子 で許可されている唯一の指定子は friend inline constexpr (C++11以降) consteval (C++20以降) 、および explicit です(特に、戻り値型は許可されません)。 CV修飾子と参照修飾子 も許可されないことに注意してください:構築中のオブジェクトのconstおよびvolatileセマンティクスは、最も派生したコンストラクタの完了後にのみ有効になります。

class-name の識別子式は、以下のいずれかの形式でなければなりません:

  • クラスに対しては、識別子式は injected-class-name である直近の外側のクラスです。
  • クラステンプレートに対しては、識別子式は current instantiation を指すクラス名 (C++20まで) injected-class-name (C++20以降) である直近の外側のクラステンプレートです。
  • それ以外の場合、識別子式は、その ルックアップ コンテキストの注入クラス名が終端非修飾識別子である修飾識別子です。

メンバー初期化子リスト

クラス T の任意のコンストラクタの 関数定義 の本体は、複合ステートメントの開き括弧の前に、 メンバ初期化子リスト を含むことができます。その構文はコロン文字 : の後に、1つ以上の member-initializer をカンマ区切りで並べたもので、それぞれは以下の構文を持ちます:

メンバ 初期化子 (1)
クラス 初期化子 (2)
クラスパック 初期化子 ... (3) (C++11以降)
1) Direct-initializes the data member named by member with initializer . member can only name non-static data members.
2) クラスオブジェクトを initializer で初期化する。 class は以下のクラスのみを指定可能:
(C++11以降)
  • T の直接基底クラスまたは virtual base class 。この場合、対応する基底クラス部分オブジェクトは initializer で直接初期化される。
3) パック展開を使用して複数の基底クラスサブオブジェクトを初期化します。
member - データメンバを命名する識別子
class - クラス名
class-pack - 0個以上のクラスに展開されるパック
initializer - = で始まらない initializer
struct S
{
    int n;
    S(int);       // コンストラクタ宣言
    S() : n(7) {} // コンストラクタ定義:
                  // ": n(7)" は初期化子リスト
                  // ": n(7) {}" は関数本体
};
S::S(int x) : n{x} {} // コンストラクタ定義: ": n{x}" は初期化子リスト
int main()
{
    S s;      // S::S() を呼び出し
    S s2(10); // S::S(int) を呼び出し
}

説明

コンストラクタには名前がなく、直接呼び出すことはできません。これらは 初期化 が行われる際に呼び出され、初期化の規則に従って選択されます。 explicit 指定子を持たないコンストラクタは converting constructors です。 constexpr 指定子を持つコンストラクタはその型を literal type にします。引数なしで呼び出すことができるコンストラクタは default constructors です。同じ型の別のオブジェクトを引数として取るコンストラクタは copy constructors move constructors です。

コンストラクタの関数本体を形成する複合文の実行が開始される前に、すべての直接基底クラス、仮想基底クラス、および非静的データメンバーの初期化が完了します。メンバー初期化子リストは、これらのサブオブジェクトの非デフォルト初期化を指定する場所です。デフォルト初期化できない基底クラスや、デフォルト初期化 または デフォルトメンバー初期化子 によって初期化できない非静的データメンバー(存在する場合) (C++11以降) 、例えば参照型やconst修飾型のメンバーなどについては、メンバー初期化子を指定する必要があります。 (クラステンプレートのインスタンス化における非静的データメンバーのデフォルトメンバー初期化子は、メンバーの型または初期化子が依存型の場合、無効となる可能性があることに注意してください。) (C++11以降) メンバー初期化子 またはデフォルトメンバー初期化子 (C++11以降) を持たない 匿名共用体 または バリアントメンバー に対しては初期化は実行されません。

初期化子( class が仮想基底クラスを指定する場合)は、構築中のオブジェクトの最も派生したクラスではない任意のクラスの構築中は無視されます。

initializer 内に現れる名前はコンストラクタのスコープで評価されます:

class X
{
    int a, b, i, j;
public:
    const int& r;
    X(int i)
      : r(a) // X::r を X::a への参照として初期化
      , b{i} // X::b をパラメータ i の値で初期化
      , i(i) // X::i をパラメータ i の値で初期化
      , j(this->i) // X::j を X::i の値で初期化
    {}
};

メンバー初期化子からスローされる例外は、 関数 try ブロック によって処理される場合があります。

非静的データメンバが デフォルトメンバ初期化子 を持ち、かつメンバ初期化子リストにも現れる場合、メンバ初期化子が使用され、デフォルトメンバ初期化子は無視されます:

struct S
{
    int n = 42;   // default member initializer
    S() : n(7) {} // will set n to 7, not 42
};
(C++11以降)

参照メンバーはメンバー初期化子リスト内で一時オブジェクトに束縛できません:

struct A
{
    A() : v(42) {} // エラー
    const int& v;
};

注: 同じことが default member initializer にも適用されます。

構築および破棄中の操作

構築中または破棄中のオブジェクトに対して、メンバー関数( 仮想メンバー関数 を含む)を呼び出すことができます。同様に、構築中または破棄中のオブジェクトは typeid または dynamic_cast のオペランドとなることができます。

ただし、以下の評価のいずれかでこれらの操作が実行される場合、動作は未定義です:

(C++26以降)
  • 基底クラスのすべての member-initializer が完了する前に メンバ初期化子リストが評価されること

委譲コンストラクタ

メンバ初期化子リストにおいてクラス名自体が class-or-identifier として現れる場合、そのリストはその単一のメンバ初期化子のみで構成されなければならない。このようなコンストラクタは 委譲コンストラクタ として知られ、初期化子リストの唯一のメンバによって選択されるコンストラクタは ターゲットコンストラクタ と呼ばれる。

この場合、ターゲットコンストラクタはオーバーロード解決によって選択され最初に実行され、その後制御が委譲コンストラクタに戻りその本体が実行される。

委譲コンストラクタは再帰的であってはならない。

class Foo
{
public: 
    Foo(char x, int y) {}
    Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int)
};

継承コンストラクタ

using 宣言 を参照。

(C++11以降)

初期化順序

メンバー初期化子リスト内の順序は無関係であり、実際の初期化順序は以下の通りです:

1) コンストラクタが最も派生したクラスのものである場合、仮想基底クラスは、基底クラス宣言の深さ優先左から右への走査(左から右は基底指定子リスト内での出現順を指す)で現れる順序で初期化されます。
2) 次に、直接基底クラスは、このクラスの基底指定子リストに現れる左から右の順序で初期化されます。
3) 次に、非静的データメンバはクラス定義内での宣言順に初期化されます。
4) 最後に、コンストラクタの本体が実行されます。

(注:もし初期化順序が異なるコンストラクタのメンバ初期化子リスト内での出現順によって制御されていた場合、 デストラクタ は構築順序の逆順で破棄が行われることを保証できなくなる。)

注記

機能テストマクロ 標準 機能
__cpp_delegating_constructors 200604L (C++11) 委譲コンストラクタ

#include <fstream>
#include <string>
#include <mutex>
struct Base
{
    int n;
};   
struct Class : public Base
{
    unsigned char x;
    unsigned char y;
    std::mutex m;
    std::lock_guard<std::mutex> lg;
    std::fstream f;
    std::string s;
    Class(int x) : Base{123}, // 基底クラスの初期化
        x(x),     // x(メンバ)はx(パラメータ)で初期化
        y{0},     // yは0で初期化
        f{"test.cc", std::ios::app}, // これはmとlgの初期化後に実行される
        s(__func__), // 初期化リストはコンストラクタの一部なので__func__が利用可能
        lg(m),    // lgは既に初期化済みのmを使用
        m{}       // ここでは最後に現れるが、mはlgより先に初期化される
    {}            // 空の複合文
    Class(double a) : y(a + 1),
        x(y), // xはyより先に初期化されるため、ここの値は不定
        lg(m)
    {} // 基底クラスの初期化子はリストに現れないため
       // デフォルト初期化される(Base()を使用した場合の値初期化とは異なる)
    Class()
    try // 関数tryブロックは関数本体(初期化リストを含む)の前に開始
      : Class(0.0) // 委譲コンストラクタ
    {
        // ...
    }
    catch (...)
    {
        // 初期化中に例外が発生
    }
};
int main()
{
    Class c;
    Class c1(1);
    Class c2(0.1);
}

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の仕様 正しい仕様
CWG 194 C++98 コンストラクタの宣言子構文では最大1つの関数指定子のみ許可されていた
(例: コンストラクタを inline explicit と宣言できなかった)
複数の関数指定子が許可される
CWG 257 C++98 抽象クラスが仮想基底クラスに対するメンバー初期化子を
提供すべきかどうかが規定されていなかった
不要であると規定され、
そのようなメンバー初期化子は
実行時に無視される
CWG 263 C++98 コンストラクタの宣言子構文により
コンストラクタをフレンドとして宣言することが禁止されていた
コンストラクタを
フレンドとして宣言することが許可される
CWG 1345 C++98 デフォルトメンバー初期化子を持たない無名共用体のメンバーは
デフォルト初期化されていた
初期化されない
CWG 1435 C++98 コンストラクタの宣言子構文における「クラス名」の
意味が不明確だった
構文を特殊化された関数宣言子構文に変更
CWG 1696 C++98 参照メンバーが一時オブジェクトで初期化できた
(その寿命はコンストラクタの終了時に終了する)
そのような初期化は
不適格となる

参考文献

  • C++23標準 (ISO/IEC 14882:2024):
  • 11.4.5 コンストラクタ [class.ctor]
  • 11.9.3 基底クラスとメンバの初期化 [class.base.init]
  • C++20規格 (ISO/IEC 14882:2020):
  • 11.4.4 コンストラクタ [class.ctor]
  • 11.10.2 基底クラスとメンバの初期化 [class.base.init]
  • C++17規格 (ISO/IEC 14882:2017):
  • 15.1 コンストラクタ [class.ctor]
  • 15.6.2 基底クラスとメンバの初期化 [class.base.init]
  • C++14 標準 (ISO/IEC 14882:2014):
  • 12.1 コンストラクタ [class.ctor]
  • 12.6.2 基底クラスとメンバの初期化 [class.base.init]
  • C++11標準 (ISO/IEC 14882:2011):
  • 12.1 コンストラクタ [class.ctor]
  • 12.6.2 基底クラスとメンバの初期化 [class.base.init]
  • C++98標準 (ISO/IEC 14882:1998):
  • 12.1 コンストラクタ [class.ctor]
  • 12.6.2 基底クラスとメンバの初期化 [class.base.init]

関連項目