名稱空間
變體
操作

引用初始化

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

將引用繫結到物件。

目錄

[編輯] 語法

[編輯] 非列表初始化
T & ref = target ;

T & ref ( target );

(1)
T && ref = target ;

T && ref ( target );

(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 & ref { arg1, arg2, ... };

(1)
T && ref = { arg1, arg2, ... };

T && ref { arg1, arg2, ... };

(2)
func-refpar ({ arg1, arg2, ... }); (3)
[編輯] 指定列表初始化 (C++20起)
T & ref = {.des1 = arg1 , .des2 { arg2 } ... };

T & ref {.des1 = arg1 , .des2 { arg2 } ... };

(1)
T && ref = {.des1 = arg1 , .des2 { arg2 } ... };

T && ref {.des1 = arg1 , .des2 { arg2 } ... };

(2)
func-refpar ({.des1 = arg1 , .des2 { arg2 } ... }); (3)

T 的引用可以用 T 型別的物件、T 型別的函式或隱式轉換為 T 的物件進行初始化。一旦初始化,引用不能被重新繫結(改變)以引用另一個物件。

引用在以下情況中初始化:

1) 當宣告具有初始化器的具名左值引用變數時。
2) 當宣告具有初始化器的具名右值引用變數時。
3) 在函式呼叫表示式中,當函式引數具有引用型別時。
4)return 語句中,當函式返回引用型別時。 如果返回的引用繫結到臨時表示式的結果,則程式格式錯誤。(C++26起)
5) 當使用成員初始化器初始化引用型別的非靜態資料成員時。

[編輯] 解釋

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, ... - 初始化列表中的初始化器

[編輯] 定義

對於兩個型別 T1T2

  • 給定 T1T2 的 cv-不合格版本分別為 U1U2,如果 U1U2 相似,或者 U1U2基類,則 T1T2 引用相關
  • 如果型別為“指向 T2 的指標”的純右值可以透過標準轉換序列轉換為型別為“指向 T1 的指標”,則 T1T2 引用相容

[編輯] 初始化規則

如果引用初始化使用普通或指定(C++20起)列表初始化,則遵循列表初始化的規則。

(C++11 起)

對於非列表引用初始化,給定 target 的型別為 U,引用要麼直接繫結target,要麼繫結到從 target 轉換而來的 T 型別的值。首先考慮直接繫結,然後是間接繫結;如果兩者都不可用,則程式格式錯誤。

在所有使用兩種型別的引用相容關係來建立引用繫結有效性的情況中,如果標準轉換序列會格式錯誤,那麼需要這種繫結的程式就是格式錯誤的。

[編輯] 直接繫結

如果滿足以下所有條件:

  • 要初始化的引用是左值引用。
  • target 是非位域左值。
  • TU 引用相容。

則引用繫結到 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 是類型別。
  • TU 不引用相關。
  • target 可以轉換為 V 型別的左值,使得 TV 引用相容。

則引用繫結到轉換的左值結果,或其適當的基類子物件。

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 起)
  • TU 引用相容。

則引用繫結到 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 是一個純右值,則對其應用臨時實體化,將純右值的型別視為調整後的型別 P

  • P 是透過將 T 的 cv 限定符新增到 target 的型別(即 U)而調整而來的。

在這種情況下,引用繫結到結果物件,或其適當的基類子物件。

(C++17 起)

否則,如果滿足以下所有條件:

  • U 是類型別。
  • TU 不引用相關。
  • target 可以轉換為 V 型別的值 v,使得 TV 引用相容,其中 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

如果轉換結果是純右值,則對其應用臨時實體化,將純右值的型別視為調整後的型別 P

  • P 是透過將 T 的 cv 限定符新增到轉換結果的型別而調整而來的。

在這種情況下,引用繫結到結果物件,或其適當的基類子物件。

(C++17 起)

[編輯] 間接繫結

如果直接繫結不可用,則考慮間接繫結。在這種情況下,T 不能與 U 引用相關。

如果 TU 是類型別,則使用複製初始化 T 型別物件的規則考慮使用者定義轉換。如果相應的非引用複製初始化格式錯誤,則程式格式錯誤。然後,呼叫轉換函式的結果(如非引用複製初始化所述)用於直接初始化引用。對於此直接初始化,不考慮使用者定義轉換。

否則,建立 T 型別的臨時物件並從 target 進行復制初始化。然後引用繫結到該臨時物件。

(C++17 前)

否則,target 被隱式轉換為“cv-不合格 T”型別的純右值。應用臨時實體化轉換,將純右值的型別視為 T,並將引用繫結到結果物件。

(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_caststatic_castdynamic_castreinterpret_cast 轉換,它將這些表示式之一轉換為指代運算元所指定的物件或其完整物件或其子物件的泛左值(顯式轉換表示式被解釋為這些轉換的序列),
  • 形式為 cond ? e1 : e2條件表示式,它是一個泛左值,其中 e1e2 是這些表示式之一,或
  • 形式為 x, e內建逗號表示式,它是一個泛左值,其中 e 是這些表示式之一。

此生命週期規則有以下例外:

  • 繫結到 return 語句中函式返回值的臨時物件不會延長其生命週期:它在返回表示式結束時立即銷燬。這樣的 return 語句總是返回一個懸空引用。
(直到 C++26)
  • 繫結到函式呼叫中引用引數的臨時物件存在,直到包含該函式呼叫的完整表示式結束:如果函式返回的引用超出完整表示式的生命週期,它將成為一個懸空引用。
  • 繫結到 new 表示式中使用的初始化器中的引用的臨時物件存在,直到包含該 new 表示式的完整表示式結束,而不是像被初始化物件那樣長時間。如果被初始化物件超出完整表示式的生命週期,其引用成員將成為懸空引用。
(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 允許間接繫結使用引用相關型別 已禁止

[編輯] 另請參閱