La risposta di erbe (prima che venisse modificato) in realtà ha dato un buon esempio di un tipo che non dovrebbe essere mobile: std::mutex
.
Il tipo di mutex nativo del sistema operativo (ad esempio pthread_mutex_t
su piattaforme POSIX) potrebbe non essere "invariante di posizione", il che significa che l'indirizzo dell'oggetto fa parte del suo valore. Ad esempio, il sistema operativo potrebbe mantenere un elenco di puntatori a tutti gli oggetti mutex inizializzati. Se std::mutex
contenesse un tipo di mutex del sistema operativo nativo come membro di dati e l'indirizzo del tipo nativo deve rimanere fisso (poiché il sistema operativo mantiene un elenco di puntatori ai suoi mutex), l'uno o l'altro std::mutex
dovrebbe archiviare il tipo di mutex nativo sull'heap in modo che rimanga a la stessa posizione quando spostato tra gli std::mutex
oggetti o std::mutex
non deve muoversi. Memorizzarlo sull'heap non è possibile, perché a std::mutex
ha un constexpr
costruttore e deve essere idoneo all'inizializzazione costante (ovvero inizializzazione statica) in modo che un globalestd::mutex
è garantito per essere costruito prima che inizi l'esecuzione del programma, quindi il suo costruttore non può usarlo new
. Quindi l'unica opzione rimasta è quella std::mutex
di essere immobili.
Lo stesso ragionamento si applica ad altri tipi che contengono qualcosa che richiede un indirizzo fisso. Se l'indirizzo della risorsa deve rimanere fisso, non spostarlo!
C'è un altro argomento per non muoversi, std::mutex
che è che sarebbe molto difficile farlo in sicurezza, perché dovresti sapere che nessuno sta cercando di bloccare il mutex nel momento in cui viene spostato. Dato che i mutex sono uno dei mattoni che puoi usare per prevenire le gare di dati, sarebbe sfortunato se non fossero al sicuro contro le gare stesse! Con un immobile std::mutex
conosci le uniche cose che chiunque può farci una volta che è stato costruito e prima che sia stato distrutto è bloccarlo e sbloccarlo, e quelle operazioni sono esplicitamente garantite come thread-safe e non introdurre gare di dati. Lo stesso argomento si applica agli std::atomic<T>
oggetti: a meno che non possano essere spostati atomicamente, non sarebbe possibile spostarli in modo sicuro, un altro thread potrebbe tentare di chiamarecompare_exchange_strong
sull'oggetto nel momento in cui viene spostato. Quindi un altro caso in cui i tipi non dovrebbero essere mobili è quello in cui sono elementi costitutivi di basso livello di codice concorrente sicuro e devono garantire l'atomicità di tutte le operazioni su di essi. Se il valore dell'oggetto può essere spostato su un nuovo oggetto in qualsiasi momento, dovrai utilizzare una variabile atomica per proteggere ogni variabile atomica in modo da sapere se è sicuro utilizzarla o è stata spostata ... e una variabile atomica per proteggere quella variabile atomica e così via ...
Penso che generalizzerei per dire che quando un oggetto è solo un puro pezzo di memoria, non un tipo che funge da supporto per un valore o astrazione di un valore, non ha senso spostarlo. Tipi fondamentali come int
non possono muoversi: spostarli è solo una copia. Non puoi strappare le budella da un int
, puoi copiarne il valore e quindi impostarlo su zero, ma è ancora un int
con un valore, sono solo byte di memoria. Ma un int
è ancora mobilenei termini della lingua perché una copia è un'operazione di spostamento valida. Per i tipi non copiabili, tuttavia, se non si desidera o non è possibile spostare il pezzo di memoria e non è possibile copiarne il valore, non è mobile. Un mutex o una variabile atomica è una posizione specifica della memoria (trattata con proprietà speciali), quindi non ha senso spostarsi e non è nemmeno copiabile, quindi non è mobile.