4

Effective Modern C++的第 12 条中,Scott Meyers 编写了以下类来展示在引用限定符上重载成员函数是多么有用:

class Widget {
public:
    using DataType = std::vector<double>;
    …
    DataType& data() &            // for lvalue Widgets
    { return values; }            // return lvalue

    DataType data() &&            // for rvalue Widgets
    { return std::move(values); } // return rvalue
    …
private:
    DataType values;
};

这似乎很清楚: nownon_temp_obj.data()将调用第一个重载并返回对之后仍然存在的对象成员的引用,而make_temp_obj().data()通过值返回对象的成员,该成员在该表达式完成后立即死亡。

这是我的第一个问题:关于&&重载,考虑到我们按值返回,为什么return std::move(values);不只是?return values;

然而,在勘误表中,迈耶斯写道

让成员函数的右值引用重载data返回一个右值的更好方法是让它返回一个右值引用。这将避免为返回值创建临时对象,并且与data第 84 页顶部附近的原始接口的按引用返回一致。

我解释为建议改变

    DataType data() &&
    { return std::move(values); }

    DataType&& data() &&
    { return std::move(values); }

但我不明白原因,特别是鉴于这个答案几乎让我相信这本书的版本是正确的,而勘误表是错误的。

所以我的第二个问题是:谁是对的?

4

1 回答 1

4

values is an object member and an lvalue, so if you just return values directly, it will be copied to the return value, not moved. The point of the && ref-qualified overload is to avoid making an unnecessary copy. return std::move(values) accomplishes this by casting values to an rvalue, so that it gets moved from instead of copied.

For the second part of your question: both have their advantages and disadvantages. As the answer you linked notes, returning by value from the && overload avoids lifetime issues, since the returned object will have its lifetime extended if a reference is immediately bound to it. On the other hand, returning by value could destroy the value of values unexpectedly. For instance:

DataType Widget::data() &&
{ return std::move(values); }

void func() {
    Widget foo;
    std::move(foo).data(); // Moves-constructs a temporary from
                           // foo::value, which is then immediately
                           // destroyed.
    auto bar = foo.data(); // Oops, foo::value was already moved from
                           // above and its value is likely gone.
}
于 2020-11-03T22:22:12.610 回答