Câu trả lời của Herb (trước khi nó được chỉnh sửa) thực sự đã đưa ra một ví dụ điển hình về loại không nên di chuyển : std::mutex
.
Loại mutex gốc của HĐH (ví dụ: pthread_mutex_t
trên nền tảng POSIX) có thể không phải là "bất biến vị trí" nghĩa là địa chỉ của đối tượng là một phần của giá trị. Ví dụ, HĐH có thể giữ một danh sách các con trỏ tới tất cả các đối tượng mutex được khởi tạo. Nếu std::mutex
chứa loại mutex OS gốc là thành viên dữ liệu và địa chỉ của kiểu bản địa phải được cố định (vì HĐH duy trì một danh sách các con trỏ tới mutexes của nó) thì std::mutex
sẽ phải lưu trữ kiểu mutex gốc trên heap để nó ở lại cùng một vị trí khi di chuyển giữa std::mutex
các đối tượng hoặc std::mutex
không được di chuyển. Lưu trữ nó trên heap là không thể, bởi vì một std::mutex
người constexpr
xây dựng và phải đủ điều kiện để khởi tạo liên tục (nghĩa là khởi tạo tĩnh) để toàn cầustd::mutex
được đảm bảo được xây dựng trước khi bắt đầu thực thi chương trình, vì vậy hàm tạo của nó không thể sử dụng new
. Vì vậy, lựa chọn duy nhất còn lại là std::mutex
bất động.
Lý do tương tự áp dụng cho các loại khác có chứa một cái gì đó yêu cầu một địa chỉ cố định. Nếu địa chỉ của tài nguyên phải cố định, đừng di chuyển nó!
Có một lập luận khác cho việc không di chuyển std::mutex
, đó là sẽ rất khó để làm điều đó một cách an toàn, bởi vì bạn cần biết rằng không ai đang cố gắng khóa mutex tại thời điểm nó được di chuyển. Vì mutexes là một trong những khối xây dựng mà bạn có thể sử dụng để ngăn chặn các cuộc đua dữ liệu, sẽ thật đáng tiếc nếu chúng không an toàn trước chính các cuộc đua! Với một bất động, std::mutex
bạn biết những điều duy nhất bất cứ ai có thể làm với nó một khi nó đã được xây dựng và trước khi nó bị phá hủy là khóa nó và mở khóa nó, và những hoạt động đó được đảm bảo rõ ràng là an toàn cho luồng và không giới thiệu các cuộc đua dữ liệu. Lập luận tương tự này áp dụng cho std::atomic<T>
các đối tượng: trừ khi chúng có thể được di chuyển nguyên tử, không thể di chuyển chúng một cách an toàn, một luồng khác có thể đang cố gắng gọicompare_exchange_strong
trên đối tượng ngay tại thời điểm nó được di chuyển. Vì vậy, một trường hợp khác mà các loại không nên di chuyển được là ở chỗ chúng là các khối xây dựng mức thấp của mã đồng thời an toàn và phải đảm bảo tính nguyên tử của tất cả các hoạt động trên chúng. Nếu giá trị đối tượng có thể được chuyển sang một đối tượng mới bất cứ lúc nào bạn cần sử dụng biến nguyên tử để bảo vệ mọi biến số nguyên tử để bạn biết liệu có an toàn khi sử dụng nó hay nó đã được di chuyển ... và một biến nguyên tử để bảo vệ biến nguyên tử đó, v.v.
Tôi nghĩ rằng tôi sẽ khái quát để nói rằng khi một đối tượng chỉ là một mảnh bộ nhớ thuần túy, không phải là một loại đóng vai trò là người nắm giữ một giá trị hoặc sự trừu tượng của một giá trị, thì việc di chuyển nó sẽ không có ý nghĩa gì. Các loại cơ bản như int
không thể di chuyển: di chuyển chúng chỉ là một bản sao. Bạn không thể rip can đảm ra khỏi một int
, bạn có thể sao chép giá trị của nó và sau đó đặt nó là không, nhưng nó vẫn là int
với một giá trị, nó chỉ byte của bộ nhớ. Nhưng một int
vẫn có thể di chuyểntrong các điều khoản ngôn ngữ vì một bản sao là một hoạt động di chuyển hợp lệ. Tuy nhiên, đối với các loại không thể sao chép, nếu bạn không muốn hoặc không thể di chuyển mảnh bộ nhớ và bạn cũng không thể sao chép giá trị của nó, thì nó không thể di chuyển được. Một mutex hoặc một biến nguyên tử là một vị trí cụ thể của bộ nhớ (được xử lý bằng các thuộc tính đặc biệt) vì vậy không có ý nghĩa để di chuyển, và cũng không thể sao chép, vì vậy nó không thể di chuyển được.