指向常量的左值引用

回顾:普通左值引用的限制

《左值引用》中,我们指出:普通左值引用只能绑定到可修改的左值。因此以下代码非法:

int main()
{
    const int x{ 5 }; // x 是不可修改的(const)左值
    int& ref{ x };    // 错误:ref 无法绑定到不可修改的左值
}

若允许这种绑定,就能通过非常量引用修改 const 变量,破坏常量语义。

指向常量的左值引用

若在声明左值引用时使用 const 关键字,即可将该引用声明为指向常量的左值引用(简称“常量引用”或“const 引用”)。常量引用可以绑定到不可修改的左值:

int main()
{
    const int x{ 5 };
    const int& ref{ x }; // 合法:ref 是指向 const int 的引用
}

由于常量引用把被引用对象视为 const,因此只能读取而不能修改:

#include <iostream>

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

    std::cout << ref << '\n'; // OK:读取
    ref = 6;                  // 错误:无法通过常量引用修改
}

用常量引用绑定可修改左值

常量引用同样能绑定到可修改的左值;此时,通过引用访问该对象时,它被视为 const

int main()
{
    int x{ 5 };
    const int& ref{ x }; // 合法:绑定到可修改左值 x

    std::cout << ref << '\n'; // OK:读取
    ref = 7;                  // 错误:不能通过 ref 修改
    x = 6;                   // OK:仍可通过原标识符修改
}

最佳实践
除非需要修改被引用对象,否则应优先使用指向常量的左值引用

用常量引用绑定右值

常量引用甚至可绑定到右值

int main()
{
    const int& ref{ 5 }; // 合法:5 是右值
    std::cout << ref << '\n'; // 输出 5
}

此时编译器会创建一个临时对象并用右值初始化,常量引用即绑定到该临时对象。

用常量引用绑定不同类型

只要源值能隐式转换为目标类型,常量引用也可绑定:

int main()
{
    const double& r1{ 5 };  // 临时 double 用 5 初始化,r1 绑定临时
    std::cout << r1 << '\n'; // 5

    char c{ 'a' };
    const int& r2{ c };     // 临时 int 用 'a' 初始化,r2 绑定临时
    std::cout << r2 << '\n'; // 97(int)
}

示例 1:临时 doubleint 5 初始化;
示例 2:临时 intchar ‘a’ 初始化。

关键要点
若类型不匹配,编译器会生成同引用类型的临时对象并绑定;此时引用实际指向临时副本而非原对象,后续对原对象的修改不会反映到引用中。

示例(警示):

short bombs{ 1 };
const int& you{ bombs }; // 绑定的是临时 int(1)
--bombs;                 // bombs 变为 0
if (you) std::cout << "Boom!\n"; // 仍打印 Boom!

绑定到临时对象时的生命周期延长

临时对象通常在本表达式结束时销毁;但直接绑定到常量引用时,其生命周期延长至与引用一致,避免悬垂引用:

int main()
{
    const int& ref{ 5 }; // 临时对象生命周期延长至 ref 作用域结束
    std::cout << ref << '\n'; // 安全
} // ref 与临时对象同时销毁

关键要点
常量引用可绑定:可修改左值、不可修改左值、右值,灵活性远高于非常量左值引用。

(进阶提示)
生命周期延长仅适用于直接绑定;函数返回的 const 引用不触发此规则,详见 12.12 课。

constexpr 引用(可选)

在引用前加 constexpr 可使其在常量表达式中使用,但只能绑定到静态存储期对象(全局或 static 局部变量):

int g_x{ 5 };

int main()
{
    constexpr int& ref1{ g_x }; // OK:全局
    static int s_x{ 6 };
    constexpr int& ref2{ s_x }; // OK:static 局部
    int x{ 6 };
    constexpr int& ref3{ x };   // 错误:非 static
}

若目标为 const 对象,需同时写 constexprconst

static const int s_x{ 6 };
constexpr const int& ref{ s_x };

因限制较多,constexpr 引用实际使用较少。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"