CppCoreGuidelines F.16 入参:复制成本低的用值传递;其他用 const 引用
25 April 2022
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
类型。