c++ - relacionales - ¿Por qué std:: vector tiene dos operadores de asignación?
sobrecarga de operadores c++ pdf (2)
En realidad, hay tres operadores de asignación definidos:
vector& operator=( const vector& other );
vector& operator=( vector&& other );
vector& operator=( std::initializer_list<T> ilist );
Su sugerencia vector& vector::operator=(vector other)
usa el modismo copiar y cambiar. Eso significa que cuando se llama al operador, el vector original se copiará en el parámetro, copiando cada elemento del vector. Entonces esta copia se intercambiará con this
. El compilador podría eludir esa copia, pero esa elisión de copia es opcional, la semántica de movimiento es estándar.
Puede usar esa expresión idiomática para reemplazar el operador de asignación de copiado:
vector& operator=( const vector& other ) {
swap(vector{other}); // create temporary copy and swap
return *this;
}
Siempre que se copie cualquiera de los elementos, esta función se lanzará también.
Para implementar el operador de asignación de movimiento simplemente deje de lado la copia:
vector& operator=( vector&& other ) {
swap(other);
return *this;
}
Como swap()
nunca arroja, tampoco lo hará el operador de asignación de movimiento.
La initializer_list
assignment también se puede implementar fácilmente utilizando el operador de asignación de movimiento y un temporal anónimo:
vector& operator=( std::initializer_list<T> ilist ) {
return *this = vector{ilist};
}
Usamos el operador de asignación de movimiento. Como una consecuencia, la operación de asignación initializer_list solo arrojará, cuando se arroje una de las instancias del elemento.
Como dije, el compilador podría eludir la copia para la tarea de copia. Pero el compilador no está obligado a implementar esa optimización. Está obligado a implementar la semántica de movimientos.
Desde 2011, tenemos asignaciones de copiar y mover. Sin embargo, esta respuesta argumenta de manera bastante convincente que, para las clases de gestión de recursos, solo se necesita un operador de asignación. Para std::vector
, por ejemplo, esto se vería como
vector& vector::operator=(vector other)
{
swap(other);
return*this;
}
El punto importante aquí es que el argumento se toma por valor. Esto significa que en el momento en que se ingresa el cuerpo de la función propiamente dicha, gran parte del trabajo ya ha sido realizado por la construcción de other
(por constructor de movimiento si es posible, de lo contrario por constructor de copia). Por lo tanto, esto implementa automáticamente tanto la asignación de copia como la de desplazamiento.
Si esto es correcto, ¿por qué ( al menos según esta documentación ) std::vector
no se implementa de esta manera?
editar para explicar cómo funciona esto. Considere lo que le sucede a other
en el código anterior en los siguientes ejemplos
void foo(std::vector<bar> &&x)
{
auto y=x; // other is copy constructed
auto z=std::move(x); // other is move constructed, no copy is ever made.
// ...
}
Si no se puede copiar el tipo de elemento, o si el contenedor no cumple la garantía de excepción fuerte, un operador de asignación de copias puede evitar la asignación en el caso de que el objeto de destino tenga capacidad suficiente:
vector& operator=(vector const& src)
{
clear();
reserve(src.size()); // no allocation if capacity() >= src.size()
uninitialized_copy_n(src.data(), src.size(), dst.data());
m_size = src.size();
}