Direct-initialization
明示的なコンストラクタ引数のセットからオブジェクトを初期化します。
目次 |
構文
T
object
(
arg
);
T
object
|
(1) | ||||||||
T
object
{
arg
};
|
(2) | (C++11以降) | |||||||
T
(
other
)
T
|
(3) | ||||||||
static_cast<
T
>(
other
)
|
(4) | ||||||||
new
T
(
args, ...
)
|
(5) | ||||||||
Class
::
Class
()
:
member
(
args, ...
)
{
...
}
|
(6) | ||||||||
[
arg
]() {
...
}
|
(7) | (C++11以降) | |||||||
説明
direct-initialization(直接初期化)は以下の状況で実行されます:
直接初期化の効果は以下の通りです:
-
If
Tis an array type,
|
(C++20 まで) |
struct A { explicit A(int i = 0) {} }; A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A() A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1] // from {} selected explicit constructor |
(C++20 から) |
-
If
Tis a class type,
|
(C++17以降) |
-
-
Tのコンストラクタが調査され、オーバーロード解決によって最適な一致が選択されます。その後、コンストラクタが呼び出されてオブジェクトが初期化されます。
-
struct B { int a; int&& r; }; int f(); int n = 10; B b1{1, f()}; // OK, lifetime is extended B b2(1, f()); // well-formed, but dangling reference B b3{1.0, 1}; // error: narrowing conversion B b4(1.0, 1); // well-formed, but dangling reference B b5(1.0, std::move(n)); // OK |
(C++20以降) |
-
それ以外の場合、
Tが非クラス型であり、ソース型がクラス型である場合、ソース型とその基底クラス(存在する場合)の変換関数を調査し、オーバーロード解決によって最適な一致を選択します。選択されたユーザー定義変換を使用して、初期化子式を初期化対象のオブジェクトに変換します。 -
それ以外の場合、
Tが bool であり、ソース型が std::nullptr_t である場合、初期化されたオブジェクトの値は false となります。 -
それ以外の場合、必要に応じて
標準変換
を使用して、
other
の値を
TのCV修飾なしバージョンに変換し、初期化対象のオブジェクトの初期値は(変換された可能性のある)その値となります。
注記
直接初期化はコピー初期化よりも制限が緩和されています: コピー初期化は非 explicit コンストラクタと非明示的なユーザー定義 conversion functions のみを考慮しますが、直接初期化はすべてのコンストラクタとすべてのユーザー定義変換関数を考慮します。
直接初期化構文 (1) (丸括弧を使用)による変数宣言と function declaration の間で曖昧性が生じた場合、コンパイラは常に関数宣言を選択します。この曖昧性解決規則は直感的でない場合があり、 most vexing parse と呼ばれています。
#include <fstream> #include <iterator> #include <string> int main() { std::ifstream file("data.txt"); // 以下は関数宣言です: std::string foo1(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // これはfoo1という関数を宣言しており、戻り値型はstd::string、 // 第1引数はstd::istreambuf_iterator<char>型で名前は"file"、 // 第2引数は名前がなくstd::istreambuf_iterator<char>()型であり、 // これは関数ポインタ型std::istreambuf_iterator<char>(*)()に書き換えられます // C++11以前の修正(変数を宣言するため)- 引数の1つを括弧で囲む: std::string str1((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // C++11以降の修正(変数を宣言するため)- いずれかの引数にリスト初期化を使用: std::string str2(std::istreambuf_iterator<char>{file}, {}); }
例
#include <iostream> #include <memory> #include <string> 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