引用初始化

来自cppreference.com
< cpp‎ | language

绑定引用到对象

目录

[编辑] 语法

T & ref = object ;

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

T & ref ( object ) ;

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

(1)

T && ref = object ;

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

T && ref ( object ) ;

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

(2) (C++11 起)
给定 R fn ( T & arg );R fn ( T && arg );

fn ( object )

fn ( { arg1, arg2, ... } )

(3)
T & fn ()T && fn ()

return object ;

(4)
给定 Class 内的 T & ref ;T && ref ;

Class::Class(...) : ref( object) {...}

(5)

[编辑] 解释

能以 T 类型对象、 T 类型函数或可隐式转换到 T 的对象初始化到 T 的引用。一旦初始化,则不能更改引用使之引用另一对象。

在下列情形初始化引用:

1) 以初始化器声明具名左值引用变量时
2) 以初始化器声明具名右值引用变量时
3) 函数调用表达式中,函数参数拥有引用类型时
4) return 语句中,函数返回引用类型时
5)成员初始化器初始化引用类型的非静态数据成员

引用初始化的效果是:

  • 若初始化器是花括号初始化器列表 { arg1, arg2, ... } ,则遵循列表初始化
  • 否则,若引用是左值引用:
  • object 是左值表达式,且其类型为 T 或从 T 派生,而且有相等或更少的 cv 限定,则引用被绑定到左值表达式所标识的对象或其基类子对象。
double d = 2.0;
double& rd = d;        // rd 引用 d
const double& rcd = d; // rcd 引用 d
struct A {};
struct B : A {} b;
A& ra = b;             // ra 引用 b 中的 A 子对象
const A& rca = b;      // rca 引用 b 中的 A 子对象
  • 否则,若 object 的类型与 T 不相同且不从它派生,而 object 拥有到左值的转换函数,其类型为 T 或从 T 派生,有相等或更少的 cv 限定,则绑定引用到转换函数所返回的左值所标识的对象(或到其基类子对象)。
struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir 指代 B::operator int& 的结果
  • 否则,若引用是到 const 的左值引用或右值引用 (C++11 起)
  • object 是非位域右值或函数左值,且其类型是 T 或从 T 派生,拥有相等或更少 cv 限定,则绑定引用到初始化器表达式的值或其基类子对象(在实质化临时量后,若需要) (C++17 起)
struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // 到 B 右值的 A 子对象。
A&& rra = f();       // 同上
 
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // 直接绑定到 i2
  • 否则,若 object 的类型不同于 T 且不从它派生,而 object 拥有到右值或函数左值的转换函数,其类型为 T 或从 T 派生,且拥有有相等或更少的 cv 限定,则绑定引用到转换函数的结果或到其基类子对象(在实质化临时量后,若需要) (C++17 起)
struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // 绑定到转换结果的 A 子对象
B&& rrb = x;    // 直接绑定到转换的结果
  • 否则,隐式转换 objectT 。绑定引用到转换(在实质化临时量后) (C++17 起)的结果。若 object (或若由用户定义转换函数进行转换,则为转换函数的结果)拥有 T 类型或从 T 派生的类型,则它必须拥有等于或少于 T 的 cv 限定,而若引用为右值引用,则它必须不是左值 (C++11 起)
struct A { operator volatile int&(); } a;
struct B { operator int&(); } b;
const int& r = a; // 错误:不能从转换结果直接初始化 r ,
                  // 因为转换结果拥有类型 "volatile int&"
int&& r2 = b; // 错误:转换结果拥有类型 "int&"
 
const std::string& rs = "abc"; // rs 指代从字符数组复制初始化的临时量
const double& rcd2 = 2;        // rcd2 指代值为 2.0 的临时量
int i3 = 2;
double&& rrd3 = i3;            // rrd3 指代值为 2.0 的临时量

[编辑] 临时量生存期

无论引用被绑定到临时量还是临时量的基类子对象,临时量的生存期都被延续以匹配引用的生存期,除了下列例外:

  • return 语句中绑定到函数返回值的临时量不被续命:它被立即于函数尾销毁。这种函数始终返回悬垂引用。
  • 在构造函数初始化器列表中绑定到引用成员的临时量只持续到构造函数退出前,而非对象存在期间(注意:这种初始化从 DR 1696 开始是病式)。
(C++14 前)
  • 在函数调用中绑定到函数参数的临时量,存在到含该函数调用的完整表达式结尾为止:若函数返回一个引用,而其生命长于完整表达式,则它成为悬垂引用。
  • 绑定到用于 new 表达式的初始化器中的引用的临时量,存在到含该 new 表达式的完整表达式结尾为止,而非被初始化对象的存在期间。若被初始化对象的声明长于完整表达式,则其引用成员成为悬垂引用。
  • 绑定到用直接初始化语法(括号),而非列表初始化语法(花括号)初始化的聚合体引用成员中引用的临时量,存在直至含该初始化器的完整表达式末尾为止。
struct A {
  int&& r;
};
A a1{7}; // OK :续命
A a2(7); // 良式,但有悬垂引用
(C++20 起)

总而言之,临时量的生存期不能以进一步“传递”延续:从绑定了该临时量的引用初始化的第二引用不影响临时量的生存期。

[编辑] 注意

引用仅在函数参数声明、函数返回类型声明、类成员声明及带 extern 指定符处不与初始化器一同出现。

[编辑] 示例

#include <utility>
#include <sstream>
struct S {
    int mi;
    const std::pair<int,int>& mp; // 引用成员
};
 
void foo(int) {}
 
struct A {};
struct B : A {
    int n;
    operator int&() { return n; };
};
 
B bar() {return B(); }
 
//int& bad_r;      // 错误:无初始化器
extern int& ext_r; // OK
 
int main(){
//  左值
    int n = 1;
    int& r1 = n;                    // 到对象 n 的左值引用
    const int& cr(n);               // 引用可以有更多 cv 限定
    volatile int& cv{n};            // 可使用任何初始化器语法
    int& r2 = r1;                   // 另一到对象 n 的左值引用
//  int& bad = cr;                  // 错误:更少 cv 限定
    int& r3 = const_cast<int&>(cr); // 需要 const_cast
 
    void (&rf)(int) = foo;  // 到函数的左值引用
    int ar[3];
    int (&ra)[3] = ar;      // 到数组的左值引用
 
    B b;
    A& base_ref = b;        // 到基类子对象的左值引用
    int& converted_ref = b; // 到转换结果的左值引用
 
//  右值
//  int& bad = 1;        // 错误:不能绑定左值引用到右值
    const int& cref = 1; // 绑定到右值
    int&& rref = 1;      // 绑定到右值
 
    const A& cref2 = bar(); // 到 B 临时量的 A 子对象的引用
    A&& rref2 = bar();      // 相同
 
    int&& xref = static_cast<int&&>(n); // 直接绑定到 n
//  int&& copy_ref = n;                 // 错误:不能绑定到左值
    double&& copy_ref = n;              // 绑定到值为 1.0 的右值临时量
 
// 临时量生存期上的限制
    std::ostream& buf_ref = std::ostringstream() << 'a'; // ostringstream 临时量
                      // 被绑定到 operator<< 的左运算数,但其生存期在分号结束,
                      // 故 buf_ref 为悬垂引用。
 
    S a { 1, {2, 3} };         // 绑定临时量 pair {2,3} 到引用成员 a.mp 并延长其生存期以匹配 a
    S* p = new S{ 1, {2, 3} }; // 绑定临时量 pair {2,3} 到引用成员 p->mp ,
                               // 但其生存期在分号结束
                               // p->mp 是悬垂引用
    delete p;
}


[编辑] 参阅