Namespaces
Variants

Direct-initialization

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

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

目次

構文

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

説明

direct-initialization(直接初期化)は以下の状況で実行されます:

1) 空でない括弧で囲まれた式のリスト または波括弧初期化リスト (C++11以降) による初期化 。
2) 非クラス型のオブジェクトの単一の波括弧囲み初期化子による初期化 (注: クラス型および波括弧初期化リストのその他の使用法については、 list-initialization を参照) (C++11以降) .
3) function-style cast または括弧付き式リストによる prvalue temporaryの初期化 (C++17まで) prvalueの結果オブジェクトの初期化 (C++17以降)
4) static_cast 式による prvalue一時オブジェクト (C++17まで) prvalueの結果オブジェクト (C++17以降) の初期化
5) 初期化子を伴うnew式による動的記憶域期間を持つオブジェクトの初期化。
6) 基底クラスまたは非静的メンバーのコンストラクタ initializer list による初期化。
7) ラムダ式においてコピーによって捕捉された変数からのクロージャオブジェクトメンバの初期化。

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

  • If T is an array type,
  • プログラムは ill-formed である。
(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 T is a class type,
  • 初期化子が prvalue 式であり、その型が T と同じクラス(CV修飾を無視)である場合、その初期化子式自体が、そこから具体化された一時オブジェクトではなく、宛先オブジェクトの初期化に使用されます。
    (C++17以前では、コンパイラはこの場合prvalue一時オブジェクトからの構築を省略する可能性がありますが、適切なコンストラクタは依然としてアクセス可能でなければなりません: copy elision を参照)
(C++17以降)
  • T のコンストラクタが調査され、オーバーロード解決によって最適な一致が選択されます。その後、コンストラクタが呼び出されてオブジェクトが初期化されます。
  • それ以外の場合、宛先型が(CV修飾可能性のある)集成体クラスである場合、 aggregate initialization で説明されているように初期化されますが、縮小変換が許可され、指示付き初期化子は許可されず、参照に束縛された一時オブジェクトの寿命は延長されず、波括弧の省略は行われず、初期化子のない要素は value-initialized されます。
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

関連項目