CppCoreGuidelines F.16 入参:复制成本低的用值传递;其他用 const 引用
25 April 2022
理由:
值传递和 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*)
却通过常量引用传递时,报警。建议使用值传递。 - (简单)(基础)当参数通过常量引用传递,却被移动了,报警。