LearnCpp 9.4 — 对 const 的左值引用

在上一课9.3左值引用中,我们讨论了左值引用如何只能绑定到可修改的左值。这意味着以下内容是非法的:

int main()
{
    const int x { 5 }; // x是不可以修改的左值
    int& ref { x }; // error: ref 不可以绑定到不可以修改的左值
    return 0;
}

这是不允许的,因为它让我们修改常量(不可修改的左值)x通过非常量引用ref。
但是,如果我们想要创建一个常量(不可修改的左值我们想要引用这个常量怎么办?正常的左值引用(对常量值)是不行的。

对 const 的左值引用

通过const在声明左值引用时使用关键字,我们告诉左值引用将其引用的对象视为 const。这样的引用称为对 const 值的左值引用(有时称为对 const 的引用或const 引用)。
对 const 的左值引用可以绑定到不可修改的左值:

int main()
{
    const int x { 5 };    
    const int& ref { x }; // okay:

    return 0;
}

因为对 const 的左值引用将它们引用的对象视为 const,所以它们可以用于访问但不能修改被引用的值:

#include <iostream>

int main()
{
    const int x { 5 };    
    const int& ref { x }; // okay

    std::cout << ref;     // okay
    ref = 6;              // error表达式必须是不可以修改的左值

    return 0;
}

使用可修改的左值初始化对 const 的左值引用

对 const 的左值引用也可以绑定到可修改的左值。在这种情况下,被引用的对象在通过引用访问时被视为 const(即使底层对象是非常量的):

int main()
{
    int x { 5 };          
    const int& ref { x }; // okay

    std::cout << ref;     // okay
    ref = 7;              // error:我们不可以修改变量通过左值引用

    x = 6;                // okay: X是一个可修改的左值,我们仍然可以通过原始标识符对其进行修改

    return 0;
}

在上面的程序中,我们将 const 引用ref绑定到可修改的 左值x。然后我们可以使用ref访问x,但是因为ref是左值引用,我们不能修改x的值通过ref。但是,我们仍然可以直接修改 的值x(使用标识符x)。

最佳实践

除非您需要修改被引用的对象,否则const 左值引用比起左值引用优先考虑。


我的补充

const关键字用来标明一个对象是不可更改的,由于其一旦创建后不可更改,所以需要在创建时对const对象进行初始化。

  • 顶层

顶层const用来标明一个变量其本身是一个不可更改的常量。内置类型的const为顶层const。对于指针,被顶层const修饰后,不可更改指针指向的对象。
const int i = 2;//顶层const
int *const p = &i;//顶层const,不可更改p指向的对象

  • 底层

底层const用来标明一个指针或引用所指向的对象是一个不可更改常量。对于指针和引用,被底层const修改后,不可通过指针或引用修改指针指向的对象值。(可以通过其他方式修改其值)
int i = 2;
const int *p = &i;//底层const
*p = 3;//错误,不可通过被const修饰的指针修改对象值

用右值初始化对 const 的左值引用

也许令人惊讶的是,对 const 的左值引用也可以绑定到右值:

#include <iostream>

int main()
{
    const int& ref { 5 }; // okay: 5 is an rvalue

    std::cout << ref; // prints 5

    return 0;
}

发生这种情况时,将创建一个临时对象并使用右值进行初始化,并且对 const 的引用绑定到该临时对象。

临时对象(有时也称为匿名对象)是在一个表达式中为临时使用而创建的对象(然后销毁)。临时对象根本没有作用域(这是有道理的,取不到地址,因为作用域是标识符的一个属性,而临时对象没有标识符)。这意味着临时对象只能在它被创建的地方直接使用,因为没有办法在该点之外引用它。


const int& x {5}; 我觉得类似于: int a = 5; const int& x{a};

看汇编代码:

在这里插入图片描述在这里插入图片描述


临时对象通常在创建它们的表达式结束时被销毁。
但是,考虑到在初始化引用的表达式结束时,如果创建用于持有右值5的临时对象,临时对象被破坏,请考虑上述示例中会发生什么。引用ref将悬空(引用已被销毁的对象),我们将会得到未定义的行为当我们尝试访问ref.

为了避免在这种情况下出现悬空引用,C++ 有一个特殊规则:当 const 左值引用绑定到临时对象时,临时对象的生命周期会延长以匹配引用的生命周期。

#include <iostream>

int main()
{
    const int& ref { 5 }; // 临时对象的生命周期会延长以匹配引用的生命周期。

    std::cout << ref; // 我们可以安全的在这里使用

    return 0;
} // 引用和临时对象在这里销毁

在上面的示例中,当ref使用 右值进行初始化时5,会创建一个临时对象并ref绑定到该临时对象。临时对象的生命周期与 ref的生命周期相匹配。因此,我们可以安全地在下一条语句中打印 ref的值。
然后两者ref和临时对象都超出作用域至块的末尾被销毁。

关键见解
左值引用只能绑定到可修改的左值。
对 const 的左值引用可以绑定到可修改的左值、不可修改的左值和右值。这使它们成为一种更灵活的引用类型。

那么为什么 C++ 允许 const 引用绑定到右值呢?我们将在下一课中回答这个问题!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值