前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则编译边学-F.21 如果需要返回多个输出值,最好返回结构体或者tuple

C++核心准则编译边学-F.21 如果需要返回多个输出值,最好返回结构体或者tuple

作者头像
面向对象思考
发布2020-03-25 15:23:54
5190
发布2020-03-25 15:23:54
举报

F.21: To return multiple "out" values, prefer returning a struct or tuple(如果需要返回多个输出值,最好返回结构体或者tuple)

Reason(原因)

A return value is self-documenting as an "output-only" value. Note that C++ does have multiple return values, by convention of using a tuple (including pair), possibly with the extra convenience of tie at the call site. Prefer using a named struct where there are semantics to the returned value. Otherwise, a nameless tuple is useful in generic code.

返回值本身就表明了自己是一个只用作输出的值。注意C++是可以同时返回多个值的,通常是使用tuple(包括pair),调用者还可以利用tie获得额外的便利性。如果返回值有特定的含义时使用命名的结构体会更好。否则无名的tuple会更适合一般的代码。

译者注:tie是C++11导入的新特性,可以用于解构tuple元素。具体示例可以参照http://www.cplusplus.com/reference/tuple/tie/

Example(示例)

代码语言:javascript
复制
// BAD: output-only parameter documented in a comment
int f(const string& input, /*output only*/ string& output_data)
{
    // ...
    output_data = something();
    return status;
}

// GOOD: self-documenting
tuple<int, string> f(const string& input)
{
    // ...
    return make_tuple(status, something());
}
代码语言:javascript
复制
译者注:类似的返回多值的做法在其他语言(例如Python)中已经广泛使用。

C++98's standard library already used this style, because a pair is like a two-element tuple. For example, given a set<string> my_set, consider:

C++98的标准库中已经使用这种风格,因为pair就像2个元素的tuple。例如,假设有一个set<string> my_set,考虑下面的代码:

代码语言:javascript
复制
// C++98
result = my_set.insert("Hello");
if (result.second) do_something_with(result.first);    // workaround

With C++11 we can write this, putting the results directly in existing local variables:

使用C++11你可以这样写,直接将结果放入已经存在的局部变量。

代码语言:javascript
复制
Sometype iter;                                // default initialize if we haven't already
Someothertype success;                        // used these variables for some other purpose

tie(iter, success) = my_set.insert("Hello");   // normal return value
if (success) do_something_with(iter);

With C++17 we are able to use "structured bindings" to declare and initialize the multiple variables:

使用C++17,我们可以使用结构化绑定功能定义和初始化多个值:

代码语言:javascript
复制
if (auto [ iter, success ] = my_set.insert("Hello"); success) do_something_with(iter);
代码语言:javascript
复制
Exception(例外)

Sometimes, we need to pass an object to a function to manipulate its state. In such cases, passing the object by reference T& is usually the right technique. Explicitly passing an in-out parameter back out again as a return value is often not necessary. For example:

有时我们需要向函数传递一个对象以便控制这个对象的状态。在这种情况下,使用引用T&传递对象通常是正确的方式。一般不需要一方面明确地传递一个输入/输出参数,另一方面却通过返回值输出。例如:

代码语言:javascript
复制
istream& operator>>(istream& is, string& s);    // much like std::operator>>()

for (string s; cin >> s; ) {
    // do something with line
}

译者注:这里说的应该是s。is由于需要支持连续的>>,需要作为返回值返回。

Here, both s and cin are used as in-out parameters. We pass cin by (non-const) reference to be able to manipulate its state. We pass s to avoid repeated allocations. By reusing s (passed by reference), we allocate new memory only when we need to expand s's capacity. This technique is sometimes called the "caller-allocated out" pattern and is particularly useful for types, such as string and vector, that needs to do free store allocations.

这里s和cin都用作输入/输出参数。我们通过(非常量)引用传递cin以便控制它的状态。我们传递s以避免重复申请内存。通过重用s(通过引用传递),我们只是在需要扩充s的容量时重新申请内存。这个技术有时被称为“用户申请的输出”模式,这种方式特别适用于类似string和vector那样的类型,它们需要释放申请到的存储空间。

To compare, if we passed out all values as return values, we would something like this:

作为比较,如果我们使用返回值传出所有值,差不多需要这样做:

代码语言:javascript
复制
pair<istream&, string> get_string(istream& is);  // not recommended
{
    string s;
    is >> s;
    return {is, s};
}

for (auto p = get_string(cin); p.first; ) {
    // do something with p.second
}

We consider that significantly less elegant with significantly less performance.

我们认为这种做法明显不够优雅,也不够高效。

For a truly strict reading of this rule (F.21), the exception isn't really an exception because it relies on in-out parameters, rather than the plain out parameters mentioned in the rule. However, we prefer to be explicit, rather than subtle.

如果真正严格地理解这条准则(F.21), 这个例外并不是真的例外,因为它依赖于输入/输出参数,而不是本准则提到的简单的输出参数。然而我们强调的是

明确而不是隐含地传递的情况。

Note(注意)

In many cases, it may be useful to return a specific, user-defined type. For example:

很多情况下,传递一个明确的,用户定义的类型可能是有用的。例如:

代码语言:javascript
复制
struct Distance {
    int value;
    int unit = 1;   // 1 means meters
};

Distance d1 = measure(obj1);        // access d1.value and d1.unit
auto d2 = measure(obj2);            // access d2.value and d2.unit
auto [value, unit] = measure(obj3); // access value and unit; somewhat redundant
                                    // to people who know measure()
auto [x, y] = measure(obj4);        // don't; it's likely to be confusing

译者注:代码中[x,y]的用法是C++17中引入的结构化绑定(structred binding)

The overly-generic pair and tuple should be used only when the value returned represents independent entities rather than an abstraction.

一般的pair和tuple应该只被用于返回值表现独立实体(数据之间没什么内在联系)的情况,而不是表现某种抽象。

Another example, use a specific type along the lines of variant<T, error_code>, rather than using the generic tuple.

另外一个例子,使用和variant<T, error_code>类似的特定类型,而不是使用一般的tuple。

译者注:variant是C++17引入的新特性。这个例子可以看做抽象之外的另一种包含明确意义的情况。

Enforcement(实施建议)
  • Output parameters should be replaced by return values. An output parameter is one that the function writes to, invokes a non-const member function, or passes on as a non-const. 应该使用返回值代替输出参数。输出参数可以是函数写入动作的对象,调用一个非常量成员函数,或者作为一个非常量传递。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • F.21: To return multiple "out" values, prefer returning a struct or tuple(如果需要返回多个输出值,最好返回结构体或者tuple)
    • Reason(原因)
      • Example(示例)
        • Exception(例外)
          • Note(注意)
            • Enforcement(实施建议)
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档