幫手如果你看get
,輔助功能爲std::tuple
,你會發現以下過載:爲什麼得到的std ::的元組返回右值引用,而不是值
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get(tuple<Types...>&& t);
換句話說,它返回當輸入元組是右值引用本身時的右值引用。爲什麼不按價值回報,在函數體中調用move
?我的觀點如下:get的返回值將被綁定到一個引用或者一個值(它可以被綁定到我想象的任何東西,但這不應該是一個常見的用例)。如果它被綁定到一個值,那麼無論如何都會發生移動建設。所以你不會因爲回報而損失任何東西。如果你綁定到一個引用,那麼返回一個右值引用實際上可能是不安全的。爲了顯示一個例子:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double;
}
當運行時,該程序打印:
Constructed at : 0x7ffc0e12cdc0
Destructed at : 0x7ffc0e12cdc0
0
換言之,X是立即懸空參考。而如果你只是這樣寫foo:
struct foo {
Hello m_hello;
Hello get() && { return std::move(m_hello); }
};
這個問題不會發生。此外,如果你再使用FOO這樣的:
Hello x(foo().get());
它似乎並不像有任何額外的開銷是否通過值或右值引用返回。我測試過這樣的代碼,看起來它會一直只執行一次移動構建。例如。如果我添加一個成員:
Hello(Hello &&) { std::cerr << "Moved" << std::endl; }
我構建X如上,我的程序只「感動」一次不管我是否通過值或右值引用返回打印。
有沒有很好的理由我錯過了,或者這是一個疏忽?
注:這裏有一個很好的相關問題:Return value or rvalue reference?。似乎認爲在這種情況下價值回報通常是可取的,但STL出現的事實讓我很好奇STL是否忽略了這種推理,或者如果他們有特殊的理由可能不適用通常。
編輯:有人提出這個問題是Is there any case where a return of a RValue Reference (&&) is useful?的重複。不是這種情況;這個答案建議通過右值引用返回,作爲避免數據成員複製的一種方式。正如我上面詳細討論的那樣,如果您首先撥打move
,那麼無論您是按價值還是按右值參考,複製都將被忽略。
這不是遠程重複。我的答案明確而詳細地討論瞭如何通過值返回並事先調用std :: move實現與從數據成員移動相同的效果。我的問題與兩種方法的差異有關。我引用自己的問題與你引用的問題有更多的關係。在急於標註重複的內容之前,請仔細閱讀。 –
如果您按值返回 - 您創建一個副本。如果您移動它,則移動副本。如果你返回一個r值引用,你可以移動它,然後移動原始對象,或者不用移動字體處理它,那麼它的行爲就像常規引用。問題是如果你返回一個r值參考,你不會強制複製。 –
爲什麼需要移動可施工性? – Columbo