通过常量左值引用传参

与非常量左值引用的区别

非常量左值引用只能绑定到可修改的左值;而常量左值引用const T&)能够绑定:

  • 可修改的左值
  • 不可修改的(const)左值
  • 右值

因此,把形参声明为 const 引用后,可接受任意类别的实参:

#include <iostream>

void printRef(const int& y)   // y 为常量引用
{
    std::cout << y << '\n';
}

int main()
{
    int x{ 5 };
    printRef(x);   // OK:可修改左值

    const int z{ 5 };
    printRef(z);   // OK:不可修改左值

    printRef(5);   // OK:右值字面量,绑定到临时 int
}

优势:避免拷贝且禁止修改

常量引用传递带来的主要好处与非常量引用相同——避免拷贝;同时保证函数内部不能修改被引用对象:

void addOne(const int& ref)
{
    ++ref; // 错误:ref 为 const,不能修改
}

最佳实践
除非需要修改实参,否则请优先使用常量左值引用作为形参。

另外,正因为允许常量引用绑定右值,我们才能把字面量或其他右值传给引用形参的函数。

类型不同时的绑定规则

在 12.4 课中已指出:常量左值引用可绑定到可隐式转换的值,此时会生成一个临时对象供引用绑定。

示例:

#include <iostream>

void printVal(double d)
{
    std::cout << d << '\n';
}

void printRef(const double& d)
{
    std::cout << d << '\n';
}

int main()
{
    printVal(5);  // 5 → 临时 double → 拷贝给 d
    printRef(5);  // 5 → 临时 double → 直接绑定给 d
}

值传递时,转换产生的额外拷贝通常可忽略(编译器常优化掉)。
引用传递若发生转换,则可能带来一次(可能昂贵的)拷贝,需留意。

警告
若实参类型与引用类型不符,编译器会生成临时对象;引用实际指向该临时对象,而非原对象,后续对原对象的修改不会影响引用。

混合传参方式

函数的多个形参可分别采用值传递或引用传递:

#include <string>

void foo(int a, int& b, const std::string& c) {}

int main()
{
    int x{ 5 };
    const std::string s{ "Hello" };
    foo(5, x, s);   // 第 1 个值传递,第 2 个非常量引用,第 3 个常量引用
}

何时用值传递,何时用引用传递

简明规则:

  • 基本类型 / 枚举:拷贝代价低,通常值传递
  • 类类型:拷贝可能昂贵,通常常量引用传递
    若拿不准,用常量引用更稳妥。

其他常见情况

  • 值传递(高效)
    ‑ 枚举(含限定与非限定)
    ‑ 视图类 std::string_viewstd::span
    ‑ 轻量级引用类(迭代器、std::reference_wrapper
    ‑ 小型值语义类(std::pair<int,int>std::optional 等)

  • 引用传递
    ‑ 需在函数内修改实参
    ‑ 不可拷贝类型(std::ostream
    ‑ 拷贝涉及所有权转移(std::unique_ptrstd::shared_ptr
    ‑ 多态基类或可能派生类(防对象切片)

值传递与引用传递的成本对比(进阶)

  1. 参数初始化

    • 值传递:拷贝成本 ≈ 对象大小 + 额外构造开销
    • 引用传递:绑定成本 ≈ 拷贝一个指针大小
  2. 参数使用

    • 值参数:一次寄存器/内存访问
    • 引用参数:先访问引用本身,再访问被引用对象(多一次间接寻址)
  3. 优化机会

    • 值传递无别名风险,优化器可更激进
    • 引用传递需保守处理别名

“廉价拷贝”经验阈值
若对象大小 ≤ 2 × sizeof(void*) 且无额外构造开销,可视为廉价;否则倾向引用传递。

检测宏示例:

#define isSmall(T) (sizeof(T) <= 2 * sizeof(void*))

字符串形参:const std::string& vs std::string_view

现代 C++ 中,若函数只需读取字符串,应优先使用 std::string_view

void doSomething(std::string_view); // 推荐
void doSomething(const std::string&); // 次选
  • std::string_view 可无缝接受 std::stringstd::string_view、C 风格字符串/字面量,且转换代价低。
  • 仅当需要 C 风格空终止符或必须调用只接受 std::string/char* 的 API 时,才用 const std::string&

性能对比表(略)已在前文详细列出。

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

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

公众号二维码

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