我在 C Primer 中查看了一個示例,解釋了std::move. 示例如下:
int &&rr1 = 42;
int &&rr3 = std::move(rr1);
在上面代碼片段的解釋中,寫到:
呼叫 move 告訴編譯器我們有一個左值,我們希望將其視為右值。重要的是要意識到移動承諾的呼叫我們不打算
rr1再次使用,除非分配給它或銷毀它。在呼叫 move 之后,我們無法對被移動物件的值做出任何假設。
我的第一個問題是:我現在可以安全地寫作 std:: cout << rr1;嗎?由于在上面的參考段落中,寫到我們不能對移動物件的值做出任何假設,所以它是否由std::cout << rr1;安全的標準(不是 UB 或取決于實作等)保證?
這是另一個例子,
std::string str = "abc";
auto&& str2 = std::move(str);
cout << str; // is this safe(not UB or depends on implementation)
同樣,在上面的代碼片段中,使用strlike的值是否安全cout << str;?
如果是,那么這是Stanley 的 C Primer一書中的錯誤/錯誤。
uj5u.com熱心網友回復:
std::move 除了添加右值參考外,它本身不做任何事情,因此兩個示例都是安全的。
既不std::move也不通過右值參考的訪問對核心語言中的物件的有效性或狀態的任何特殊影響。
的目的std::move只是用作例如函式呼叫的引數,作為對被呼叫者的承諾,您不會關心呼叫后物件的狀態。
如果函式 eg 不修改物件的狀態,那么在將物件的狀態傳遞給帶有std::move. 由于std::move呼叫本身在任何情況下都沒有未定義的行為。
所以這本書是正確的,std::move通常應該將使用視為承諾在呼叫后不依賴物件的狀態。只是有點不清楚承諾只是一個合同,而不是核心語言的一部分。它是針對被呼叫者的,而不是針對編譯器的。
但特別是標準庫通常依賴于這個承諾。當其余的呼叫代碼錯誤地依賴于標準庫呼叫后的狀態時,這可能會導致未定義的行為。由于標準庫的特殊地位,在這種情況下,對編譯器的承諾也是如此。
合同的細節不同。例如std::unique_ptr,在移動后做出強有力的保證。以下是明確定義的,并且始終滿足斷言條件:
auto p = std::make_unique<int>(1);
auto q = std::move(p);
auto ptr = p.get();
assert(ptr == nullptr);
如果沒有這樣的特定保證,移動操作可能會導致傳遞物件的任何有效狀態,這只是一個標準約定。
uj5u.com熱心網友回復:
這兩種情況都是安全的,因為沒有移動操作(移動構造或移動分配)發生。例如,在 中auto&& str2 = std::move(str);,std::move(str)只生成一個 xvalue(右值),然后將其系結到參考str2。
特別是,std::move 產生一個 xvalue 運算式來標識它的引數 t。它完全等同于對右值參考型別的 static_cast。
另一方面,
std::string str = "abc";
auto str2 = std::move(str); // move construction happens
這是來自cppreference.com的示例:
除非另有說明,所有移出的標準庫物件都處于“有效但未指定的狀態”,這意味著物件的類不變數保持不變(因此沒有先決條件的函式,例如賦值運算子,可以安全地在物件上使用它是從):
std::vector<std::string> v; std::string str = "example"; v.push_back(std::move(str)); // str is now valid but unspecified str.back(); // undefined behavior if size() == 0: back() has a precondition !empty() if (!str.empty()) str.back(); // OK, empty() has no precondition and back() precondition is met str.clear(); // OK, clear() has no preconditions
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/393548.html
