大多数 C++ 标准库实用程序在对 rvalue 限定符进行重载时,返回 rvalue 引用。例如,std::optional 对 value()
函数有以下重载。
constexpr T& value() &;
constexpr const T & value() const &;
constexpr T&& value() &&;
constexpr const T&& value() const &&;
这样做可以在需要时移动返回值,非常好。这是一种可靠的优化。
但是,与返回rvalue相关的不确定性如何处理呢?例如(live example here 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()
的返回值时,转发引用range
并没有扩展xvalue的生命周期。这会导致绑定到已销毁的值。因此会导致未定义行为。
为什么不通过返回值来内部移动存储的对象?尤其是现在C++17有prvalue优化的情况下。
auto lck = std::lock_guard{mtx};
请注意,这与此处的问题不同:C++11 rvalues and move semantics confusion (return statement),这个问题没有提到rvalue返回容器/持有者时寿命延长问题,并且在C++17强制使用prvalues省略之前就已经被问过了。
for (auto vec_op = get_vector(); auto ele : *vec_op)
,这将只需要略微更多的语法就可以实现正确版本的迭代。 - Kerrek SBauto&&
插入初始化器中以涵盖两种情况。) - Kerrek SBauto&&
如何解决这里的问题?在这种情况下,寿命延长仍然不适用吗? - Curiousfor (auto&& x = f(); auto el : *x)
,而不必担心f
返回的是左值还是右值。 - Kerrek SB