引用初始化
將引用繫結到物件。
目錄 |
[編輯] 語法
[編輯] 非列表初始化
T & ref = target ; T |
(1) | ||||||||
T && ref = target ; T |
(2) | (C++11 起) | |||||||
func-refpar ( target ) |
(3) | ||||||||
return target ; |
(4) | (在 func-refret 的定義內部) | |||||||
Class:: Class( ...) : ref-member ( target ) { ... } |
(5) | (在 Class 的定義內部) | |||||||
[編輯] 普通列表初始化 (C++11起)
T & ref = { arg1, arg2, ... }; T |
(1) | ||||||||
T && ref = { arg1, arg2, ... }; T |
(2) | ||||||||
func-refpar ({ arg1, arg2, ... }); |
(3) | ||||||||
[編輯] 指定列表初始化 (C++20起)
T & ref = {. des1 = arg1 , . des2 { arg2 } ... }; T |
(1) | ||||||||
T && ref = {. des1 = arg1 , . des2 { arg2 } ... }; T |
(2) | ||||||||
func-refpar ({. des1 = arg1 , . des2 { arg2 } ... }); |
(3) | ||||||||
對 T
的引用可以用 T
型別的物件、T
型別的函式或隱式轉換為 T
的物件進行初始化。一旦初始化,引用不能被重新繫結(改變)以引用另一個物件。
引用在以下情況中初始化:
[編輯] 解釋
T | - | 被引用型別 |
ref | - | 要初始化的引用變數 |
target | - | 正在使用的初始化表示式 |
func-refpar | - | 一個帶有引用型別引數的函式(T & 或 T && (C++11起)) |
func-refret | - | 一個返回型別為引用型別(T & 或 T && (C++11起))的函式 |
Class | - | 類名 |
ref-member | - | Class 的引用型別(T & 或 T && (C++11起))的非靜態資料成員 |
des1, des2, ... | - | 指定符 |
arg1, arg2, ... | - | 初始化列表中的初始化器 |
[編輯] 定義
對於兩個型別 T1
和 T2
- 給定
T1
和T2
的 cv-不合格版本分別為U1
和U2
,如果U1
與U2
相似,或者U1
是U2
的基類,則T1
與T2
引用相關。 - 如果型別為“指向
T2
的指標”的純右值可以透過標準轉換序列轉換為型別為“指向T1
的指標”,則T1
與T2
引用相容。
[編輯] 初始化規則
如果引用初始化使用普通或指定(C++20起)列表初始化,則遵循列表初始化的規則。 |
(C++11 起) |
對於非列表引用初始化,給定 target 的型別為 U
,引用要麼直接繫結到 target,要麼繫結到從 target 轉換而來的 T
型別的值。首先考慮直接繫結,然後是間接繫結;如果兩者都不可用,則程式格式錯誤。
在所有使用兩種型別的引用相容關係來建立引用繫結有效性的情況中,如果標準轉換序列會格式錯誤,那麼需要這種繫結的程式就是格式錯誤的。
[編輯] 直接繫結
如果滿足以下所有條件:
- 要初始化的引用是左值引用。
- target 是非位域左值。
-
T
與U
引用相容。
則引用繫結到 target,或其適當的基類子物件。
double d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A {}; struct B : A {} b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b
否則,如果滿足以下所有條件:
- 要初始化的引用是左值引用。
-
U
是類型別。 -
T
與U
不引用相關。 - target 可以轉換為
V
型別的左值,使得T
與V
引用相容。
則引用繫結到轉換的左值結果,或其適當的基類子物件。
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // ir refers to the result of B::operator int&
否則,如果待初始化的引用是左值引用,且 T
未被 const 限定或被 volatile 限定,則程式格式錯誤。
double& rd2 = 2.0; // error: not an lvalue and reference is not const int i = 2; double& rd3 = i; // error: type mismatch and reference is not const
否則,如果滿足以下所有條件:
- target 是以下任何類別的物件:
|
(C++11 前) |
|
(C++11 起) (C++17 前) |
|
(C++17 起) |
-
T
與U
引用相容。
則引用繫結到 target,或其適當的基類子物件。
struct A {}; struct B : A {}; extern B f(); const A& rca2 = f(); // bound to the A subobject of the B rvalue. A&& rra = f(); // same as above int i2 = 42; int&& rri = static_cast<int&&>(i2); // bound directly to i2
如果 target 是一個純右值,則對其應用臨時實體化,將純右值的型別視為調整後的型別
在這種情況下,引用繫結到結果物件,或其適當的基類子物件。 |
(C++17 起) |
否則,如果滿足以下所有條件:
-
U
是類型別。 -
T
與U
不引用相關。 - target 可以轉換為
V
型別的值 v,使得T
與V
引用相容,其中 v 屬於以下任何類別:
|
(C++11 前) |
|
(C++11 起) (C++17 前) |
|
(C++17 起) |
則引用繫結到轉換的結果,或其適當的基類子物件。
struct A {}; struct B : A {}; struct X { operator B(); } x; const A& r = x; // bound to the A subobject of the result of the conversion B&& rrb = x; // bound directly to the result of the conversion
如果轉換結果是純右值,則對其應用臨時實體化,將純右值的型別視為調整後的型別
在這種情況下,引用繫結到結果物件,或其適當的基類子物件。 |
(C++17 起) |
[編輯] 間接繫結
如果直接繫結不可用,則考慮間接繫結。在這種情況下,T
不能與 U
引用相關。
如果 T
或 U
是類型別,則使用複製初始化 T
型別物件的規則考慮使用者定義轉換。如果相應的非引用複製初始化格式錯誤,則程式格式錯誤。然後,呼叫轉換函式的結果(如非引用複製初始化所述)用於直接初始化引用。對於此直接初始化,不考慮使用者定義轉換。
否則,建立 |
(C++17 前) |
否則,target 被隱式轉換為“cv-不合格 |
(C++17 起) |
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 int i3 = 2; double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0
[編輯] 臨時物件的生命週期
每當引用繫結到臨時物件或其子物件時,臨時物件的生命週期將延長以匹配引用的生命週期(檢查臨時物件生命週期異常),其中臨時物件或其子物件由以下表達式之一表示:
|
(C++17 前) |
(C++17 起) |
- 帶括號的表示式 (e),其中 e 是這些表示式之一,
- 形式為 a[n] 或 n[a] 的內建下標表達式,其中 a 是一個數組並且是這些表示式之一,
- 形式為 e.m 的類成員訪問表示式,其中 e 是這些表示式之一,並且 m 指示物件型別的非靜態資料成員,
- 形式為 e.*mp 的指向成員的指標操作,其中 e 是這些表示式之一,並且 mp 是指向資料成員的指標,
- 不帶使用者定義轉換的
const_cast
、static_cast
、dynamic_cast
或reinterpret_cast
轉換,它將這些表示式之一轉換為指代運算元所指定的物件或其完整物件或其子物件的泛左值(顯式轉換表示式被解釋為這些轉換的序列), - 形式為 cond ? e1 : e2 的條件表示式,它是一個泛左值,其中 e1 或 e2 是這些表示式之一,或
- 形式為 x, e 的內建逗號表示式,它是一個泛左值,其中 e 是這些表示式之一。
此生命週期規則有以下例外:
|
(直到 C++26) |
- 繫結到函式呼叫中引用引數的臨時物件存在,直到包含該函式呼叫的完整表示式結束:如果函式返回的引用超出完整表示式的生命週期,它將成為一個懸空引用。
|
(C++11 起) |
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference |
(C++20 起) |
通常,臨時物件的生命週期不能透過“傳遞它”來進一步延長:從臨時物件所繫結的引用變數或資料成員初始化的第二個引用不會影響其生命週期。
[編輯] 注意
引用僅在函式引數宣告、函式返回型別宣告、類成員宣告以及帶有extern
說明符時才不帶初始化器出現。
在解決 CWG issue 1696 之前,允許臨時物件繫結到建構函式初始化器列表中的引用成員,並且它僅在建構函式退出時才存在,而不是與物件一樣長。自 CWG 1696 以來,這種初始化是格式錯誤的,儘管許多編譯器仍然支援它(一個值得注意的例外是 clang)。
[編輯] 示例
#include <sstream> #include <utility> struct S { int mi; const std::pair<int, int>& mp; // reference member }; void foo(int) {} struct A {}; struct B : A { int n; operator int&() { return n; } }; B bar() { return B(); } //int& bad_r; // error: no initializer extern int& ext_r; // OK int main() { // Lvalues int n = 1; int& r1 = n; // lvalue reference to the object n const int& cr(n); // reference can be more cv-qualified volatile int& cv{n}; // any initializer syntax can be used int& r2 = r1; // another lvalue reference to the object n // int& bad = cr; // error: less cv-qualified int& r3 = const_cast<int&>(cr); // const_cast is needed void (&rf)(int) = foo; // lvalue reference to function int ar[3]; int (&ra)[3] = ar; // lvalue reference to array B b; A& base_ref = b; // reference to base subobject int& converted_ref = b; // reference to the result of a conversion // Rvalues // int& bad = 1; // error: cannot bind lvalue ref to rvalue const int& cref = 1; // bound to rvalue int&& rref = 1; // bound to rvalue const A& cref2 = bar(); // reference to A subobject of B temporary A&& rref2 = bar(); // same int&& xref = static_cast<int&&>(n); // bind directly to n // int&& copy_ref = n; // error: can't bind to an lvalue double&& copy_ref = n; // bind to an rvalue temporary with value 1.0 // Restrictions on temporary lifetimes // std::ostream& buf_ref = std::ostringstream() << 'a'; // the ostringstream temporary was bound to the left operand // of operator<< but its lifetime ended at the semicolon so // the buf_ref is a dangling reference S a {1, {2, 3}}; // temporary pair {2, 3} bound to the reference member // a.mp and its lifetime is extended to match // the lifetime of object a S* p = new S{1, {2, 3}}; // temporary pair {2, 3} bound to the reference // member p->mp, but its lifetime ended at the semicolon // p->mp is a dangling reference delete p; // Imitate [[maybe_unused]] applied to the following variables: [](...){} ( cv, r2, r3, rf, ra, base_ref, converted_ref, a, cref, rref, cref2, rref2, copy_ref, xref ); }
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 391 | C++98 | 用類型別的 const 限定引用初始化 右值可能建立一個臨時物件,並且該類的建構函式 需要將右值複製到該臨時物件中。 |
沒有臨時物件 被建立,建構函式 不被要求 |
CWG 450 | C++98 | const 限定陣列的引用不能用引用相容的陣列右值初始化。 用引用相容的陣列右值初始化。 |
允許 |
CWG 589 | C++98 | 引用不能直接繫結到陣列或類右值 | 允許 |
CWG 656 | C++98 | 用非引用相容但具有到引用相容型別轉換函式的型別初始化的 const 限定型別引用被繫結到從轉換函式的返回值(或其基類子物件)複製的臨時物件 引用相容但具有轉換函式到引用 相容型別被繫結到從轉換函式的返回值(或其基類子物件)複製的臨時物件 值(或其基類子物件)的臨時副本 |
直接繫結到返回值 值(或其基類 子物件)直接 |
CWG 1287 | C++11 | 從類型別 target 到另一個引用相容型別的轉換隻能是隱式的 引用相容型別只能是隱式的 |
允許顯式 轉換 |
CWG 1295 | C++11 | 引用可以繫結到位域亡值 | 已禁止 |
CWG 1299 | C++98 | 臨時物件的定義不明確 | 已明確 |
CWG 1571 | C++98 | 間接繫結中的使用者定義轉換沒有考慮 target 的型別 繫結中的使用者定義轉換沒有考慮 target 的型別 |
已考慮 |
CWG 1604 | C++98 | 間接繫結中未考慮使用者定義轉換 | 已考慮 |
CWG 2352 | C++98 | 引用相容性未考慮限定符轉換 | 已考慮 |
CWG 2481 | C++17 | 間接繫結中臨時實體化結果型別未新增 cv-限定符 間接繫結中臨時實體化結果型別未新增 cv-限定符 |
已新增 |
CWG 2657 | C++17 | 間接繫結中臨時實體化結果型別未新增 cv-限定符 直接繫結中臨時實體化結果型別未新增 cv-限定符 |
已新增 |
CWG 2801 | C++98 | 允許間接繫結使用引用相關型別 | 已禁止 |