25 April 2022

C++ 核心指南目录

F.16: For “in” parameters, pass cheaply-copied types by value and others by reference to const

理由

值拷贝传递参数和 const 引用传递参数都表明函数不会修改参数,这样,就允许用右值初始化这些参数。

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

例子

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

例子

void f2(string s);         // bad: potentially expensive
void f4(const int& x);     // bad: overhead on access

什么情况需要用右值传递“只读入参”呢?

  • 如果函数任何情况下都会移动参数,用右值引用&&。详见 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

注意

我们可以假定一个引用肯定指向了一个有效的对象,语言层面不存在空引用的情况。如果需要表达一个值可能为空,请使用指针。另外std::optional也能用来表示“没有数值”的情况。

强化

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

例外

如果要表达共享所有权的情况,可以用shared_ptr类型。