名前空間
変種
操作

直接初期化

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

明示的なコンストラクタ引数の集合からオブジェクトを初期化します。

目次

[編集] 構文

T object ( arg );

T object ( arg1, arg2, ... );

(1)
T object { arg }; (2) (C++11以上)
T ( other )

T ( arg1, arg2, ... )

(3)
static_cast< T >( other ) (4)
new T(args, ...) (5)
Class::Class() : member(args, ...) { ... } (6)
[arg](){ ... } (7) (C++11以上)

[編集] 説明

直接初期化は以下の状況で行われます。

1) 括弧で囲まれた空でない式または波括弧初期化子リスト (C++11以上)のリスト付きの初期化。
2) 波括弧で囲まれた単一の初期化子付きの非クラス型のオブジェクトの初期化 (ノート: クラス型や波括弧初期化子リストの他の使用方法については、リスト初期化を参照してください)。
3) 関数形式のキャストによる、または括弧で囲まれた式のリスト付きの、 prvalue の一時オブジェクト (C++17未満)prvalue の結果オブジェクト (C++17以上)の初期化。
4) static_cast 式による prvalue の一時オブジェクト (C++17未満)prvalue の結果オブジェクト (C++17以上)の初期化。
5) 空でない初期化子付きの new 式による、動的記憶域期間を持つオブジェクトの初期化。
6) コンストラクタの初期化子リストによる、基底クラスまたは非静的データメンバの初期化。
7) ラムダ式における、コピーキャプチャされた変数からのクロージャオブジェクトのメンバの初期化。

直接初期化の効果は以下の通りです。

  • T が配列型の場合、
  • プログラムは ill-formed です。
(C++20未満)
  • 配列は集成体初期化と同様に初期化されます。 ただし、縮小変換が許され、初期化子を持たないあらゆる要素は値初期化されます。
struct A { explicit A(int i = 0) {} };
A a[2](A(1)); // OK、 a[0] を A(1) で、 a[1] を A() で初期化します。
A b[2]{A(1)}; // エラー、 {} 選択された explicit コンストラクタからの a[1] の暗黙のコピーリスト初期化。
(C++20以上)
  • T がクラス型の場合、
  • 初期化子が prvalue 式で���り、その型が T と同じクラス (cv 修飾は無視します) であれば、初期化子の式それ自身 (そこから具体化された一時オブジェクトではなく) が宛先のオブジェクトを初期化するために使用されます。 コピー省略を参照してください。
    (C++17未満では、この場合、コンパイラは prvalue の一時オブジェクトからの構築を省略することもできましたが、対応するコンストラクタはアクセス可能でなければなりませんでした。 コピー省略を参照してください)。
(C++17以上)
  • T のコンストラクタが調べられ、オーバーロード解決によってベストマッチが選択されます。 そしてそのコンストラクタがオブジェクトを初期化するために呼ばれます。
  • そうでなく、宛先の型が集成体クラス (cv 修飾されていても構いません) の場合は、集成体初期化で説明されているように初期化されます。 ただし、縮小変換が許され、指示付き初期化子は許されず、参照に束縛された一時オブジェクトは生存期間が延長されず、波括弧省略はなく、初期化子を持たないあらゆる要素は値初期化されます。
struct B {
  int a;
  int&& r;
};
 
int f();
int n = 10;
 
B b1{1, f()};               // OK、生存期間は延長されます。
B b2(1, f());               // well-formed ですが、ダングリング参照です。
B b3{1.0, 1};               // エラー、縮小変換。
B b4(1.0, 1);               // well-formed ですが、ダングリング参照です。
B b5(1.0, std::move(n));    // OK。
(C++20以上)
  • そうでなく、 T は非クラス型だけれどもソース型がクラス型の場合、そのソース型およびその基底クラスの変換関数 (もしあれば) が調べられ、オーバーロード解決によってベストマッチが選択されます。 そして選択されたユーザ定義変換が初期化子式を初期化中のオブジェクトに変換するために使用されます。
  • そうでなく、 Tbool であり、ソース型が std::nullptr_t の場合、初期化されたオブジェクトの値は false です。
  • そうでなければ、 other の値を T の cv 修飾されていないバージョンに変換するために標準変換が (必要であれば) 使用され、初期化中のオブジェクトの初期値はその (必要に応じて変換された) 値になります。

[編集] ノート

直接初期化はコピー初期化よりも寛容です。 コピー初期化は非 explicit コンストラクタと非 explicit ユーザ定義変換関数しか考慮しないのに対して、直接初期化はすべてのコンストラクタとすべてのユーザ定義変換関数を考慮します。

直接初期化の構文 (1) (丸括弧付き) を用いた変数宣言と関数宣言との間で曖昧な場合、コンパイラは常に関数宣言を選択します。 この曖昧性解消ルールは非直感的なことが多く、最も苛立たしいパースと呼ばれています。

#include <iterator>
#include <string>
#include <fstream>
int main()
{
    std::ifstream file("data.txt");
    // 以下は関数の宣言です。
    std::string str(std::istreambuf_iterator<char>(file),
                    std::istreambuf_iterator<char>());
    // これは、戻り値が std::string 型で、
    // 第1引数が「file」という名前の std::istreambuf_iterator<char> 型で、
    // 第2引数が名前のない std::istreambuf_iterator<char>() 型
    // (関数ポインタ型 std::istreambuf_iterator<char>(*)() に書き直されます)
    // の、 str という名前の関数を宣言します。
 
    // C++11 前の修正方法。 いずれかの引数の周りに追加の括弧を付けます。
    std::string str( (std::istreambuf_iterator<char>(file) ),
                      std::istreambuf_iterator<char>());  
    // C++11 後の修正方法。 いずれかの引数をリスト初期化にします。
    std::string str(std::istreambuf_iterator<char>{file}, {});
}

同様に、関数形式のキャスト式 (3) を最も左の部分式として持つ式文と宣言文との間で曖昧な場合、その曖昧性はそれを宣言として扱うことによって解決されます。 この曖昧性解消は純粋に構文的なものです。 文中に現れる名前の意味は、それらが型名であるかどうか以外、考慮されません。

struct M { };
struct L { L(M&); };
 
M n;
void f() {
    M(m); // 宣言。 M m; と同等です。
    L(n); // ill-formed な宣言。
    L(l)(m); // これでもまだ宣言です。
}

[編集]

#include <string>
#include <iostream>
#include <memory>
 
struct Foo {
    int mem;
    explicit Foo(int n) : mem(n) {}
};
 
int main()
{
    std::string s1("test"); // const char* からのコンストラクタ。
    std::string s2(10, 'a');
 
    std::unique_ptr<int> p(new int(1)); // OK。 explicit コンストラクタが使用できます。
//  std::unique_ptr<int> p = new int(1); // エラー。 コンストラクタが explicit です。
 
    Foo f(2); // f は直接初期化されます。
              // コンストラクタ引数 n は右辺値 2 からコピー初期化され、
              // f.mem は引数 n から直接初期化されます。
//  Foo f2 = 2; // エラー。 コンストラクタが explicit です。
 
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

出力:

test aaaaaaaaaa 1 2

[編集] 関連項目