La respuesta de hierba (antes de que fuera editado) en realidad le dio un buen ejemplo de un tipo que no debe ser móvil: std::mutex
.
El tipo de mutex nativo del sistema operativo (por ejemplo, pthread_mutex_t
en plataformas POSIX) podría no ser "invariante de ubicación", lo que significa que la dirección del objeto es parte de su valor. Por ejemplo, el sistema operativo puede mantener una lista de punteros a todos los objetos mutex inicializados. Si std::mutex
contiene un tipo de mutex del sistema operativo nativo como miembro de datos y la dirección del tipo nativo debe permanecer fija (porque el sistema operativo mantiene una lista de punteros a sus mutex), entonces cualquiera de std::mutex
los dos tendría que almacenar el tipo de mutex nativo en el montón para que permaneciera en la misma ubicación cuando se mueve entre std::mutex
objetos o std::mutex
no debe moverse. No es posible almacenarlo en el montón, porque a std::mutex
tiene un constexpr
constructor y debe ser elegible para una inicialización constante (es decir, una inicialización estática) para questd::mutex
se garantiza que se construirá antes de que comience la ejecución del programa, por lo que su constructor no puede usarlo new
. Entonces, la única opción que queda es std::mutex
ser inamovible.
El mismo razonamiento se aplica a otros tipos que contienen algo que requiere una dirección fija. Si la dirección del recurso debe permanecer fija, ¡no la mueva!
Hay otro argumento para no moverse, std::mutex
que es que sería muy difícil hacerlo de manera segura, porque necesitaría saber que nadie está tratando de bloquear el mutex en el momento en que se está moviendo. Dado que los mutexes son uno de los bloques de construcción que puedes usar para prevenir las carreras de datos, ¡sería desafortunado si no estuvieran a salvo contra las propias carreras! Con un inmueble, std::mutex
usted sabe que lo único que cualquiera puede hacerle una vez que se ha construido y antes de que se haya destruido es bloquearlo y desbloquearlo, y esas operaciones están explícitamente garantizadas para ser seguras y no introducir carreras de datos. Este mismo argumento se aplica a los std::atomic<T>
objetos: a menos que se puedan mover atómicamente, no sería posible moverlos con seguridad, otro hilo podría estar intentando llamarcompare_exchange_strong
en el objeto justo en el momento en que se mueve. Por lo tanto, otro caso en el que los tipos no deberían ser móviles es donde son bloques de construcción de bajo nivel de código concurrente seguro y deben garantizar la atomicidad de todas las operaciones en ellos. Si el valor del objeto se pudiera mover a un nuevo objeto en cualquier momento, necesitaría usar una variable atómica para proteger cada variable atómica para que sepa si es seguro usarlo o si se ha movido ... y una variable atómica para proteger esa variable atómica, y así sucesivamente ...
Creo que generalizaría para decir que cuando un objeto es solo un recuerdo puro, no un tipo que actúa como titular de un valor o abstracción de un valor, no tiene sentido moverlo. Tipos fundamentales como int
no poder mover: moverlos es solo una copia. No puede extraer las agallas de un int
, puede copiar su valor y luego establecerlo en cero, pero sigue int
siendo un valor, solo son bytes de memoria. Pero unint
todavía es móvilen los términos del lenguaje porque una copia es una operación de movimiento válida. Sin embargo, para los tipos que no se pueden copiar, si no quiere o no puede mover la pieza de memoria y tampoco puede copiar su valor, entonces no es móvil. Un mutex o una variable atómica es una ubicación específica de la memoria (tratada con propiedades especiales), por lo que no tiene sentido moverse, y tampoco es copiable, por lo que no se puede mover.