CppCoreGuidelines F.20 需要输出值的话,返回值比输出参数更好
30 May 2022
理由:
返回值本身就是代码的显式文档。然而如果用 &
引用参数,就可能是输入输出参数,也可能只是输出参数,容易被误用。
此指南也适用于标准容器这类大对象,在返回过程中,为了保证性能,隐式的用了移动操作,避免了显式的内存管理。
如果有多个值需要返回,可以用元组或类似的多成员类型。
例子:
// OK: return pointers to elements with the value x vector<const int*> find_all_ok(const vector<int>& v, int x) { auto rv = vector<const int*>(); for(vector<int>::const_iterator it=v.begin(); it!=v.end(); it++) { if (*it == x) { rv.push_back(&*it); } } return rv; } // Bad: place pointers to elements with value x in-out void find_all_bad(const vector<int>& v, vector<const int*>& out, int x) { for(vector<int>::const_iterator it=v.begin(); it!=v.end(); it++) { if (*it == x) { out.push_back(&*it); } } } int main() { auto vv = vector<int>{1,2,3,4}; auto vvv = find_all_ok(vv, 3); for(int i=0; i<vvv.size(); i++) { cout << *vvv[i]; } cout << endl; vvv = vector<const int*>(); find_all_bad(vv, vvv, 3); for(int i=0; i<vvv.size(); i++) { cout << *vvv[i]; } return 0; }
3 3
注意
移动含有多个小对象的结构体,性能可能较差。
注意
不建议返回 const
值。返回 const
值没啥价值,并且对移动操作有影响。
// bad: that "const" is more trouble than it is worth const vector<int> fct() { return vector<int>{1,2,3}; } void g(vector<int>& vx) { // ... fct() = vx; // prevented by the "const" // ... vx = fct(); // expensive copy: move semantics suppressed by the "const" // ... }
返回值设置成 const
是为了避免不小心访问临时数据(很少发生)。添加
const
阻止移动机制,从而导致性能变差(经常发生)。
例外
如果是继承层级中的非具体类型,可通过 unique_ptr
或 shared_ptr
返回对象。
如果类型的移动成本很高(如 array<BiPOD>
),考虑先分配内存,再返回指针(如 unique_ptr
),或传入一个非 const
的目标对象,把数据填入此对象。
如果要在一个内部循环中多次调用函数,需要重用一个对象保存数据(如
std::string
std::vector
),可以设置为输入输出参数,通过引用传递。
例子
struct Package { // exceptional case: expensive-to-move object char header[16]; char load[2024 - 16]; }; Package fill(); // Bad: large return value void fill(Package&); // OK int val(); // OK void val(int&); // Bad: Is val reading its argument
强化
- 非
const
参数,改写之前没有读取操作,其实可以用低成本的返回值的方式。标注告警。 - 返回一个常量值。去除
const
,返回一个非const
值。