命名空间
变体
操作

引用初始化

来自 cppreference.cn
< 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 允许间接绑定使用引用相关类型 已禁止

[编辑] 另请参阅