25 April 2022

C++ 核心指南目录

理由:

值传递和 const 引用标明函数不会修改参数,允许用右值初始化。

“复制成本低”与机器的体系架构相关。一般 2 到 3 word 的数据(double,指针,引用)通过值传递。对于几个 word 的小对象,因为无需间接访问,值传递也比引用传递速度快。

例子

void f1(const string& s);  // OK: pass by reference to const; always cheap

void f2(string s);         // bad: potentially expensive

void f3(int x);            // OK: Unbeatable

void f4(const int& x);     // bad: overhead on access in f4()

什么情况需要用右值传递“只读输入”参数:

  • 如果函数任何情况下都会移入参数,用右值引用 && 。详见 F.18
  • 如果函数保留一份参数副本,除了可以用 const& 进行左值传递;也可以重载函数,接收 && 右值引用,并在函数体内通过 std::move “移动”到目的地。事实上, std::move 并不能移动任何东西,其功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值。从实现上讲, std::move 基本等同于一个类型转换: static_cast<T&&>(lvalue); 详见 F.18
  • 一些特殊情况,如多个“输入+复制”参数,考虑使用完美转发 std::forward<Args>(args) 。详见 F.19

例子

int multiply(int, int); // just input ints, pass by value

// suffix is input-only but not as cheap as an int, pass by const&
string& concatenate(string&, const string& suffix);

void sink(unique_ptr<widget>);  // input only, and moves ownership of the widget

避免深奥的技巧,比如:

  • 为了提高执行效率使用 T&& 传递参数。大部分关于 && 的性能优势都是错的,不可靠的。(详见 F.18 F.19)
  • 通过 const T& 返回。(详见 F.47)

例子

假设 Matrix 有移动操作(比如把元素保留在 std::vector ):

Matrix operator+(const Matrix& a, const Matrix& b)
{
    Matrix res;
    // ... fill res with the sum ...
    return res;
}

Matrix x = m1 + m2;  // move constructor

y = m3 + m3;         // move assignment

注意

The return value optimization doesn’t handle the assignment case, but the move assignment does.

引用指向有效的对象,不存在空引用的情况。如果需要表示可选值,请使用指针, std::optional 或能表示“无值”的特殊值。

强化

  • (简单)(基础)值的空间大于 2 * sizeof(void*) 却通过值传递时,报警。建议使用 const& 常量引用。
  • (简单)(基础)值的空间小于 2 * sizeof(void*) 却通过常量引用传递时,报警。建议使用值传递。
  • (简单)(基础)当参数通过常量引用传递,却被移动了,报警。