名稱空間
變體
操作

直接初始化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

使用一組顯式建構函式引數初始化物件。

目錄

[編輯] 語法

T 物件 ( arg );

T 物件 ( arg1, arg2, ... );

(1)
T 物件 { arg }; (2) (C++11 起)
T ( other )

T ( arg1, arg2, ... )

(3)
static_cast< T >( other ) (4)
new T( args, ... ) (5)
::() : 成員( args, ... ) { ... } (6)
[arg]() { ... } (7) (C++11 起)

[編輯] 解釋

直接初始化在以下情況發生

1) 使用非空括號表示式列表 或花括號初始化列表(C++11 起) 進行初始化。
2) 使用單個花括號初始化器初始化非類型別物件 (注意:對於類型別和花括號初始化列表的其他用途,請參見列表初始化(C++11 起)
3) 透過函式式轉型或帶括號的表示式列表初始化純右值臨時物件(C++17 前)純右值的結果物件(C++17 起)
4) 透過 static_cast 表示式初始化純右值臨時物件(C++17 前)純右值的結果物件(C++17 起)
5) 透過帶初始化器的 new-expression 初始化具有動態儲存期的物件。
6) 透過建構函式初始化列表初始化基類或非靜態成員。
7) 從 lambda 表示式中透過複製捕獲的變數初始化閉包物件成員。

直接初始化的效果是

  • 如果 T 是陣列型別,
  • 程式格式錯誤。
(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 起)
  • 如果 T 是類型別,
  • 如果初始化器是其型別與 T 相同的(忽略 cv-限定符)純右值表示式,則使用初始化器表示式本身而不是從其具象化的臨時物件來初始化目標物件。
    (在 C++17 之前,編譯器可能在此情況下消除純右值臨時物件的構造,但相應的建構函式必須仍然可訪問:參見複製消除
(C++17 起)
  • 檢查 T 的建構函式,並透過過載決議選擇最佳匹配。然後呼叫該建構函式來初始化物件。
  • 否則,如果目標型別是(可能帶有 cv-限定符的)聚合類,它將按照聚合初始化中描述的方式進行初始化,但允許縮小轉換,不允許指定初始化器,繫結到引用的臨時物件的生命週期不會延長,沒有花括號省略,並且任何沒有初始化器的元素都進行值初始化
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 是非類型別但源型別是類型別,則檢查源型別及其基類的轉換函式(如果有),並透過過載決議選擇最佳匹配。然後使用選定的使用者定義轉換將初始化器表示式轉換為正在初始化的物件。
  • 否則,如果 Tbool 且源型別是 std::nullptr_t,則初始化物件的值為 false
  • 否則,如果需要,使用標準轉換other的值轉換為 T 的 cv-非限定版本,並且正在初始化的物件的初始值是(可能已轉換的)值。

[編輯] 注意

直接初始化比複製初始化更寬容:複製初始化只考慮非explicit建構函式和非 explicit 使用者定義轉換函式,而直接初始化考慮所有建構函式和所有使用者定義轉換函式。

在使用直接初始化語法 (1)(帶圓括號)的變數宣告與函式宣告之間發生歧義時,編譯器總是選擇函式宣告。這種消歧規則有時是反直覺的,被稱為最令人煩惱的解析

#include <fstream>
#include <iterator>
#include <string>
 
int main()
{
    std::ifstream file("data.txt");
 
    // The following is a function declaration:
    std::string foo1(std::istreambuf_iterator<char>(file),
                     std::istreambuf_iterator<char>());
    // It declares a function called foo1, whose return type is std::string,
    // first parameter has type std::istreambuf_iterator<char> and the name "file",
    // second parameter has no name and has type std::istreambuf_iterator<char>(),
    // which is rewritten to function pointer type std::istreambuf_iterator<char>(*)()
 
    // Pre-C++11 fix (to declare a variable) - add extra parentheses around one
    // of the arguments:
    std::string str1((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
 
    // Post-C++11 fix (to declare a variable) - use list-initialization for any
    // of the arguments:
    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"); // constructor from const char*
    std::string s2(10, 'a');
 
    std::unique_ptr<int> p(new int(1));  // OK: explicit constructors allowed
//  std::unique_ptr<int> p = new int(1); // error: constructor is explicit
 
    Foo f(2); // f is direct-initialized:
              // constructor parameter n is copy-initialized from the rvalue 2
              // f.mem is direct-initialized from the parameter n
//  Foo f2 = 2; // error: constructor is explicit
 
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

輸出

test aaaaaaaaaa 1 2

[編輯] 另請參見