3 回答

TA貢獻1812條經驗 獲得超5個贊
通過使用auto&& var = <initializer>您的意思是:我將接受任何初始化程序,無論它是左值表達式還是右值表達式,我都將保留其constness。通常用于轉發(通常使用T&&)。之所以起作用,是因為“通用引用” auto&&或T&&將綁定到任何東西。
你可能會說,好吧,為什么不直接使用const auto&,因為這將也綁定到什么?使用const引用的問題在于const!您以后將無法將其綁定到任何非const引用或調用未標記的任何成員函數const。
例如,假設您要獲取一個std::vector,將迭代器帶到其第一個元素,然后以某種方式修改該迭代器指向的值:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
無論初始化表達式如何,此代碼都可以正常編譯。auto&&失敗的替代方式如下:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
因此,這auto&&很完美!auto&&在基于范圍的for循環中使用此類示例。有關更多詳細信息,請參見我的其他問題。
如果再使用std::forward您auto&&參考,以保持一個事實,即它本來無論是一個左或右值,你的代碼說:現在,我已經得到了你的對象從任何一個左或右值的表情,我想保留原來為準它valueness因此我可以最有效地使用它-這可能會使它失效。如:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
use_it_elsewhere當原始的初始值設定項是可修改的右值時,這可以消除其膽量以提高性能(避免復制)。
這意味著我們是否可以或何時可以從中竊取資源var?好吧,因為auto&&遺囑會綁定到任何東西,所以我們可能無法嘗試自行消除var漏洞-它很可能是左值甚至是const。但是std::forward,我們可以將其用于可能完全破壞其內部的其他功能。一旦這樣做,我們應該考慮var處于無效狀態。
現在,將其應用于auto&& var = foo();您的問題中給出的的情況,其中foo返回一個T按值。在這種情況下,我們肯定知道的類型var將推導為T&&。由于我們肯定知道它是右值,因此我們不需要std::forward的許可就可以竊取其資源。在這種特定情況下,知道foo按值返回,讀者應該將其讀取為:我正在對r所返回的臨時值進行右值引用foo,因此我可以很高興地將其移走。
作為附錄,我認為值得一提的是,何時出現類似“表達式some_expression_that_may_be_rvalue_or_lvalue可能會更改”的情況。所以這是一個人為的例子:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
這get_vector<T>()是一個可愛的表達式,根據泛型類型,它可以是左值或右值T。我們實質上get_vector通過的模板參數更改的返回類型foo。
當我們調用時foo<std::vector<int>>,get_vector將按global_vec值返回,從而給出一個右值表達式。或者,當我們調用時foo<std::vector<int>&>,get_vector將global_vec通過引用返回,從而產生一個左值表達式。
如果這樣做:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
正如預期的那樣,我們得到以下輸出:
2
1
2
2
如果你要改變auto&&在代碼中任何的auto,auto&,const auto&,或const auto&&那么我們不會得到我們想要的結果。
根據auto&&引用是用左值表達式還是右值表達式初始化的,更改程序邏輯的另一種方法是使用類型特征:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}

TA貢獻1803條經驗 獲得超6個贊
首先,我建議將我的答案作為側面閱讀,以逐步解釋通用引用的模板參數推導如何工作。
這是否意味著我們被允許竊取的資源var?
不必要。如果foo()突然返回參考,或者您更改了通話但忘了更新,該var怎么辦?或者,如果您使用的是通用代碼,則的返回類型foo()可能會根據您的參數而變化?
考慮auto&&與T&&in 完全相同template<class T> void f(T&& v);,因為它(幾乎?)完全一樣。當您需要傳遞或以任何方式使用它們時,如何使用函數中的通用引用?您用于std::forward<T>(v)獲取原始值類別。如果在傳遞給函數之前是左值,則在通過后將保持左值std::forward。如果是右值,它將再次變為右值(請記住,命名的右值引用是左值)。
那么,您如何var以通用方式正確使用?使用std::forward<decltype(var)>(var)。這將與std::forward<T>(v)上面的功能模板中的完全相同。如果var為T&&,則將返回右值;如果為T&,則將返回左值。
因此,在后面的話題:做什么auto&& v = f();,并std::forward<decltype(v)>(v)在代碼庫告訴我們?他們告訴我們,這v將以最有效的方式獲得并傳遞。但是請記住,在轉發了這樣的變量之后,可能會將其移出,因此在不重新設置它的情況下進一步使用它是不正確的。
就個人而言,我使用auto&&的通用代碼時,我需要一個modifyable變量。完美轉發右值正在修改,因為移動操作可能會竊取其膽量。如果我只是想偷懶(即即使我知道它也不要拼寫類型名稱)并且不需要修改(例如,僅打印范圍內的元素時),我會堅持使用auto const&。
? auto是迄今為止不同之處在于auto v = {1,2,3};將v一個std::initializer_list,而f({1,2,3})將是一個失敗的扣除。

TA貢獻1829條經驗 獲得超9個贊
考慮T具有移動構造函數的某種類型,并假設
T t( foo() );
使用該move構造函數。
現在,讓我們使用一個中間引用來捕獲來自的回報foo:
auto const &ref = foo();
這會排除使用move構造函數,因此必須復制返回值而不是移動它(即使我們std::move在此處使用,我們也無法實際通過const ref進行移動)
T t(std::move(ref)); // invokes T::T(T const&)
但是,如果我們使用
auto &&rvref = foo();
// ...
T t(std::move(rvref)); // invokes T::T(T &&)
move構造函數仍然可用。
并解決您的其他問題:
...在任何合理的情況下,您應該使用auto &&&告訴代碼的讀者...
正如Xeo所說,第一件事本質上是無論X是什么類型,我都盡可能高效地傳遞X。因此,看到在auto&&內部使用的代碼應該傳達出它將在適當的情況下在內部使用移動語義。
...就像您返回unique_ptr <>告訴您擁有專有所有權時一樣...
當函數模板接受類型為的參數時T&&,就是說它可能會移動您傳入的對象unique_ptr。接受T&&可能會刪除呼叫者的所有權(如果存在移動控制器等)。
- 3 回答
- 0 關注
- 1348 瀏覽
添加回答
舉報