命名空间
变体
操作

引用初始化

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++17* 中已弃用)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
聚合初始化
列表初始化 (C++11)  
常量初始化
引用初始化

 
 

将引用绑定到对象。

内容

[编辑] 语法

[编辑] 非列表初始化
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 - 类型为引用类型的非静态数据成员 ( T &T &&(自 C++11 起) ),属于 Class
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)从 target 的类型调整而来。

在这种情况下,引用绑定到结果对象,或绑定到其适当的基类子对象。

(自 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 转换,而没有用户定义的转换到glvalue(显式转型表达式被解释为这些转型的序列),
  • 形式为 cond ? e1 : e2条件表达式,它是 glvalue,其中 e1e2 是这些表达式之一,或者
  • 形式为 x, e内置逗号表达式,它是 glvalue,其中 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 问题 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++ 标准。

DR 应用于 已发布行为 正确行为
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 引用可以绑定到位域 xvalue 禁止
CWG 1299 C++98 临时对象的定义不明确 已明确
CWG 1571 C++98 间接绑定中的用户定义转换
没有考虑 target 的类型
已考虑
CWG 1604 C++98 间接绑定中没有考虑用户定义的转换 已考虑
CWG 2352 C++98 引用兼容性没有考虑限定转换 已考虑
CWG 2481 C++17 cv 限定符未添加到结果类型
间接绑定中临时对象实质化
已添加
CWG 2657 C++17 cv 限定符未添加到结果类型
直接绑定中临时对象实质化
已添加
CWG 2801 C++98 引用相关类型允许用于间接绑定 禁止

[编辑] 参见