30 May 2022

C++ 核心指南目录

理由:

返回值本身就是代码的显式文档。然而如果用 & 引用参数,就可能是输入输出参数,也可能只是输出参数,容易被误用。

此指南也适用于标准容器这类大对象,在返回过程中,为了保证性能,隐式的用了移动操作,避免了显式的内存管理。

如果有多个值需要返回,可以用元组或类似的多成员类型。

例子:

// 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_ptrshared_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 值。