返回值或右值引用

Return value or rvalue reference?

本文关键字:引用 返回值      更新时间:2023-10-16

在Scott Meyer的新书中,他提出了一个右值引用限定符的示例用法,如下所示:

class Widget {
private:
    DataType values;
public:
    DataType& data() &  { return values; } 
    DataType  data() && { return std::move(values); } // why DataType?
};

因此:

auto values = makeWidget().data();

移动构造values而不是复制构造它。

为什么符合 rvalue-ref 条件的data()返回 DataType 而不是 DataType&& ? 在这种情况下,auto仍然会推断出DataType(尽管decltype(auto)不会 - 但这不是更喜欢返回值而不是 ravlue 引用的唯一原因(。这个高投票的答案返回了一个右值参考,这对我来说在概念上更有意义。

DataType data() && { return std::move(values); } // why DataType?
auto values = makeWidget().data();

保存返回值的临时值将通过移动构造函数初始化,从 move(values) 复制初始化。

然后临时初始化values,但由于makeWidget().data()是一个右值(准确地说是prvalue(,移动构造函数再次被调用 - 临时作为其参数。

现在考虑复制省略

当一个无名的临时的,不绑定到任何引用,将被移动 或复制到相同 CV 非限定类型的对象中, 省略复制/移动。当构造该临时时,它是 直接在存储中构建,否则它将被移动 或复制到。当无名临时是返回的参数时 语句,这种复制 elision 的变体称为 RVO,"返回值 优化"。

因此,第二步将(大概(被完全省略,只剩下一个 - 如果返回类型是右值引用,我们无论如何都会有。

返回右值引用的问题在于,如果我们编写

auto&& values = makeWidget().data();

values将悬而未决,因为将 xvalue 绑定到引用不会延长任何内容的生命周期。当我们返回对象类型时,临时生存期会延长。

通过研究StackOverflow和技术博客终于弄明白了这一点,这里贡献了我的想法


    class Widget { 
        private: DataType values; 
        public: DataType& data() & { return values; } 
        // return rvalue, okay, ref-qualifier necessary, since values not usable after move
        DataType data() && { return std::move(values); }
        // return rvalue reference, okay
        DataType&& data_ref() && { return std::move(values); }
    };
    // note that auto is deduced to be Widget type
    // okay, rvalue returned by function call moves construct w1
    // if compiler implemented RVO copy elision, the move in move construct likely be elided
    auto w1 = Widget().data(); 
    // okay, rvalue reference returned by function call moves construct w2
    auto w2 = Widget().data_ref(); 
    // auto&& is a universal reference, deduced to be Widget&& rvalue reference
    // dangling reference, bad. temporary copy is destroyed, so w3 is a dangling rvalue reference to values in the destroyed copy
    auto&& w3 = Widget().data_ref();

如果auto&&返回右值引用,它将被破坏。在这种特殊情况下,链接的答案被破坏了。

这里的核心问题是你可以重载左值或右值,但实际上有三个值类别你可能想知道 - 值,左值和右值。C++ 在调用成员函数时不区分 value 和 rvalue,因此您无法知道返回 rvalue 引用是否正确。无论你做出哪个决定,都很容易构建它不起作用的例子。