Sử dụng lại một vùng chứa đã chuyển?


84

Cách chính xác để sử dụng lại thùng đã chuyển là gì?

std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);

// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize

container.push_back(2);
assert(container.size() == 1 && container.front() == 2);

Từ những gì tôi đã đọc trong bản nháp tiêu chuẩn C ++ 0x; ver3 dường như là một cách chính xác, vì một đối tượng sau khi di chuyển là trong một

"Trừ khi có quy định khác, các đối tượng được chuyển đến đó sẽ được đặt ở trạng thái hợp lệ nhưng không xác định."

Tôi chưa bao giờ tìm thấy bất kỳ trường hợp nào mà nó được "chỉ định khác".

Mặc dù tôi thấy ver3 hơi vòng vo và sẽ có nhiều ưu tiên cho ver1 hơn, mặc dù vec3 có thể cho phép tối ưu hóa bổ sung, nhưng mặt khác có thể dễ dẫn đến sai lầm.

Giả định của tôi có đúng không?


4
Bạn chỉ có thể gọi clear, vì nó không có điều kiện tiên quyết (và do đó không phụ thuộc vào trạng thái của đối tượng).
Nicol Bolas

@Nicol: Giả sử có một std::vectortriển khai đã lưu trữ một con trỏ tới kích thước của nó (có vẻ ngớ ngẩn, nhưng hợp pháp). Di chuyển từ vectơ đó có thể để lại con trỏ NULL, sau đó clearsẽ không thành công. operator=cũng có thể thất bại.
Ben Voigt

9
@Ben: Tôi nghĩ điều đó sẽ vi phạm phần "hợp lệ" của "hợp lệ nhưng không xác định".
ildjarn

1
@ildjarn: Tôi nghĩ nó chỉ có nghĩa là nó an toàn để chạy trình hủy.
Ben Voigt

Tôi đoán câu hỏi là "hợp lệ" là gì?
ronag

Câu trả lời:


97

Từ mục 17.3.26 của đặc tả "trạng thái hợp lệ nhưng không xác định":

một trạng thái đối tượng không được chỉ định ngoại trừ việc đáp ứng các bất biến của đối tượng và các hoạt động trên đối tượng hoạt động như được chỉ định cho kiểu của nó [Ví dụ: Nếu một đối tượng xcủa kiểu std::vector<int>ở trạng thái hợp lệ nhưng không xác định, x.empty()có thể được gọi vô điều kiện và x.front()có thể được gọi chỉ khi x.empty()trả về false. —Gửi ví dụ]

Do đó, đối tượng đang sống. Bạn có thể thực hiện bất kỳ hoạt động nào không yêu cầu điều kiện tiên quyết (trừ khi bạn xác minh điều kiện tiên quyết trước).

clear, chẳng hạn, không có điều kiện tiên quyết. Và nó sẽ trả đối tượng về trạng thái đã biết. Vì vậy, chỉ cần xóa nó và sử dụng nó như bình thường.


Tôi có thể đọc ở đâu trong tiêu chuẩn về "điều kiện tiên quyết" cho ví dụ: phương thức vectơ std ::?
ronag

1
@ronag: §23.2 chứa các bảng trong đó chúng được liệt kê.
Grizzly

2
Tôi thấy điều thú vị sau đây, open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html , họ viết "thùng chứa có thể 'rỗng hơn rỗng'".
ronag

4
@ronag: 1) Nếu vùng chứa ở trạng thái hợp lệ thì việc gọi clearlà hợp lệ. 2) Trong khi container đã trong tình trạng không xác định, gọi clearputs container vào trạng thái được quy định bởi vì nó đã yêu cầu postconditions trong tiêu chuẩn (§23.2.3 bảng 100). std::vector<T>có một lớp bất biến push_back()luôn luôn hợp lệ (miễn TCopyInsertable).
ildjarn

3
@ronag: open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html đã trích dẫn một trong những bình luận của cơ quan quốc gia về câu trích dẫn "trống rỗng hơn". Nhận xét của cơ quan quốc gia là không chính xác. N3241 đã không đề xuất trạng thái như vậy. Nếu việc triển khai std :: container có trạng thái "trống rỗng hơn" do chuyển từ, thì trạng thái đó phải là trạng thái hợp lệ (nghĩa là bạn có thể làm bất cứ điều gì với đối tượng đó mà không cần điều kiện tiên quyết).
Howard Hinnant

11

Đối tượng đang ở trạng thái hợp lệ, nhưng không xác định về cơ bản có nghĩa là mặc dù trạng thái chính xác của đối tượng không được đảm bảo, nó vẫn hợp lệ và như vậy các hàm thành viên (hoặc các hàm không thành viên) được đảm bảo hoạt động miễn là chúng không dựa vào trên đối tượng có một trạng thái nhất định.

Hàm clear()thành viên không có điều kiện tiên quyết về trạng thái của đối tượng (tất nhiên là ngoài việc nó hợp lệ) và do đó có thể được gọi trên các đối tượng được chuyển đến. Mặt khác, ví dụ, front()phụ thuộc vào vùng chứa không rỗng và do đó không thể được gọi, vì nó không được đảm bảo là không rỗng.

Do đó cả ver2 và ver3 đều ổn.


Một vectơ sẽ luôn trống, nhưng điều đó không đúng với trường hợp chung, (mảng IE)
Mooing Duck

"Một vector sẽ luôn trống", bạn căn cứ vào điều đó để làm gì?
ronag

1
@ronag: Ý tôi là tất nhiên là ver2 và ver3 (vì văn bản phải rõ ràng, đã sửa lỗi đánh máy đó
Grizzly

Điều thú vị là các điều kiện tiên quyết front()chỉ được nêu cho std::arrayvà thậm chí không có trong bảng.
Ben Voigt

1
@Ben: §23.2.3 bảng 100 nói rằng ngữ nghĩa hoạt động của front()*a.begin(), §23.2.1 / 6 nói rằng " Nếu vùng chứa trống, thìbegin() == end() " và §24.2.1 / 5 nói " Thư viện không bao giờ giả định rằng quá khứ- các giá trị cuối là có thể tham khảo được. ". Do đó, tôi nghĩ rằng các điều kiện tiên quyết front()có thể được suy ra, mặc dù nó chắc chắn có thể được làm rõ ràng hơn.
ildjarn

-8

Tôi không nghĩ bạn có thể làm BẤT CỨ ĐIỀU GÌ với một đối tượng được chuyển đến (ngoại trừ việc phá hủy nó).

Bạn không thể sử dụng swapthay thế, để có được tất cả các lợi ích của việc di chuyển nhưng để thùng chứa ở trạng thái đã biết?


+1. hoán đổi là một ý tưởng hay, mặc dù nó sẽ không hoạt động trong mọi trường hợp, ví dụ như sử dụng tự động sẽ không hoạt động. Có thể một safe_move, sử dụng hoán đổi nội bộ có thể là một ý tưởng?
ronag

5
Đó là một đối tượng sống, và bạn có thể sử dụng bất kỳ chức năng mà không có điều kiện tiên quyết (ngoài bất biến)
Mooing Duck

Mẫu chính cho std::swapcó 2 nhiệm vụ di chuyển, với mục tiêu của các nhiệm vụ đó được chuyển từ các giá trị. Đó là tội là "làm cái gì đó để một chuyển-từ đối tượng" với tôi
Caleth
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.