Odpowiedź Herba (zanim została zredagowana) faktycznie podała dobry przykład typu, którego nie można przenosić:std::mutex .
Natywny typ muteksu systemu operacyjnego (np. pthread_mutex_tNa platformach POSIX) może nie być „niezmienny względem lokalizacji”, co oznacza, że adres obiektu jest częścią jego wartości. Na przykład system operacyjny może przechowywać listę wskaźników do wszystkich zainicjowanych obiektów mutex. Jeśli std::mutexzawiera natywny typ muteksu systemu operacyjnego jako element członkowski danych, a adres typu natywnego musi pozostać niezmieniony (ponieważ system operacyjny utrzymuje listę wskaźników do swoich muteksów), wówczas każdy z nich std::mutexmusiałby przechowywać natywny typ muteksu na stercie, aby pozostał na w tej samej lokalizacji, gdy są przenoszone między std::mutexobiektami lub std::mutexnie mogą się poruszać. Przechowywanie go na stercie nie jest możliwe, ponieważ std::mutexma constexprkonstruktora i musi kwalifikować się do stałej inicjalizacji (tj. Inicjalizacji statycznej), aby globalnastd::mutex jest gwarantowane, że zostanie utworzony przed rozpoczęciem wykonywania programu, więc jego konstruktor nie może go użyć new.std::mutex
To samo rozumowanie dotyczy innych typów, które zawierają coś, co wymaga stałego adresu. Jeśli adres zasobu musi pozostać niezmieniony, nie przenoś go!
Jest jeszcze jeden argument przemawiający za tym, aby się nie ruszać, std::mutexa mianowicie, że byłoby bardzo trudno zrobić to bezpiecznie, ponieważ musiałbyś wiedzieć, że nikt nie próbuje zablokować muteksu w momencie, gdy jest przenoszony. Ponieważ muteksy są jednym z elementów budulcowych, których możesz użyć do zapobiegania wyścigom danych, byłoby niefortunne, gdyby same nie były bezpieczne przed rasami! Z nieruchomością std::mutexwiesz, że jedyną rzeczą, jaką każdy może z nią zrobić po jej zbudowaniu i zanim zostanie zniszczona, jest zablokowanie jej i odblokowanie, a operacje te są wyraźnie gwarantowane, że są bezpieczne dla wątków i nie wprowadzają wyścigów danych. Ten sam argument odnosi się do std::atomic<T>obiektów: jeśli nie można ich przesunąć atomowo, nie byłoby możliwe ich bezpieczne przeniesienie, inny wątek może próbować wywołaćcompare_exchange_strongna obiekcie w momencie, gdy jest przenoszony. Więc innym przypadkiem, w którym typy nie powinny być ruchome, jest sytuacja, w której są one niskopoziomowymi blokami konstrukcyjnymi bezpiecznego kodu współbieżnego i muszą zapewniać atomowość wszystkich operacji na nich. Jeśli wartość obiektu może zostać przeniesiona do nowego obiektu w dowolnym momencie, musisz użyć zmiennej atomowej, aby chronić każdą zmienną atomową, aby wiedzieć, czy można jej bezpiecznie używać, czy została przeniesiona ... i zmienną atomową do ochrony ta zmienna atomowa i tak dalej ...
Myślę, że uogólniłbym stwierdzenie, że kiedy obiekt jest po prostu czystą pamięcią, a nie typem, który działa jako posiadacz wartości lub abstrakcja wartości, nie ma sensu go przenosić. Podstawowe typy, takie jak intnie można się ruszać: przenoszenie ich to tylko kopia. Nie możesz wyrwać wnętrzności z an int, możesz skopiować jego wartość, a następnie ustawić ją na zero, ale nadal jest to intz wartością, to tylko bajty pamięci. Ale intjest nadal ruchomyw terminach językowych, ponieważ kopia jest prawidłową operacją przenoszenia. Jednak w przypadku typów nie do skopiowania, jeśli nie chcesz lub nie możesz przenieść fragmentu pamięci, a także nie możesz skopiować jego wartości, oznacza to, że nie można go przenieść. Muteks lub zmienna atomowa to określone miejsce w pamięci (traktowane specjalnymi właściwościami), więc nie ma sensu przenosić, a także nie można ich kopiować, więc nie można ich przenosić.