通过右值引用返回与通过值返回

Returning by rvalue reference vs returning by value

大多数 C++ 标准库实用程序 return 在为此的右值限定符上重载时通过右值引用。例如 std::optional 对 value() 函数

有以下重载
constexpr T& value() &;
constexpr const T & value() const &;
constexpr T&& value() &&;
constexpr const T&& value() const &&;

这允许在需要时移动 returned 值,很好。这是一个可靠的优化。

但是与 returning 右值相关的不确定性呢?例如(现场示例https://wandbox.org/permlink/kUqjfOWWRP6N57eS

auto get_vector() {
    auto vector = std::vector<int>{1, 2, 3};
    return std::optional{std::move(vector)};
}

int main() {
    for (auto ele : *get_vector()) {
        cout << ele << endl;
    }
}

由于基于范围的 for 循环的扩展方式,上述代码导致了未定义的行为

{
    auto && __range = range_expression ; 
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
    } 
} 

绑定到 *get_vector() 的 return 值时转发引用 range 不会延长 xvalue 的生命周期。并导致绑定到一个被破坏的值。因此导致 UB。

为什么不 return 按值并在内部移动存储的对象?特别是因为现在 C++17 有 prvalue 优化,例如

auto lck = std::lock_guard{mtx};

请注意,这与这里的问题 C++11 rvalues and move semantics confusion (return statement) 不同,这没有提到右值 returns 和 container/holders 的生命周期延长问题,并且在 C 之前被问到++17 对 prvalues

进行了强制省略

Why not return by value and internally move the stored object?

因为这可能比返回引用效率低。考虑这样一种情况,您使用返回的引用从该对象中获取另一个引用。例如 (*get_vector())[3]。对于您提议的更改,这是对原件副本的引用;目前的方式,它是对原始临时值中的值的引用。

C++ 作为一种语言并不能有效地处理对临时对象的引用的生命周期。目前的解决方案包括要么注意生命周期,要么不使用引用并拥有可能 slower/less 高效的代码。通常,标准库更愿意在性能方面犯错。