c++ ¿La Regla de tres se convierte en Regla de cinco con C ++ 11?



4 Answers

No puedo creer que nadie vinculado a this .

Básicamente el artículo defiende la "Regla de cero". No es apropiado que cite un artículo completo, pero creo que este es el punto principal:

Las clases que tienen destructores personalizados, constructores de copia / movimiento o operadores de asignación de copia / movimiento deben tratar exclusivamente con la propiedad. Otras clases no deben tener destructores personalizados, copiar / mover constructores ni copiar / mover operadores de asignación.

También este bit es IMHO importante:

Las clases comunes de "propiedad en un paquete" se incluyen en la biblioteca estándar: std::unique_ptr y std::shared_ptr . Mediante el uso de objetos de eliminación personalizados, ambos se han hecho lo suficientemente flexibles para gestionar prácticamente cualquier tipo de recurso.

c++ constructor c++11 rvalue-reference rule-of-three

Así que, después de ver esta maravillosa conferencia sobre referencias de valores, pensé que todas las clases se beneficiarían de un "constructor de movimientos" de este tipo, la template<class T> MyClass(T&& other) editaría y, por supuesto, un "operador de asignación de movimientos", la template<class T> MyClass& operator=(T&& other) como señala Philipp en su respuesta, si tiene miembros asignados dinámicamente, o generalmente almacena punteros. Al igual que usted debería tener un copy-ctor, operador de asignación y destructor si se aplican los puntos mencionados anteriormente. ¿Pensamientos?




Sí, creo que sería bueno proporcionar un constructor de movimientos para dichas clases, pero recuerda que:

  • Es solo una optimización.

    Implementar solo uno o dos del constructor de copia, operador de asignación o destructor probablemente generará errores, mientras que no tener un constructor de movimiento solo reducirá potencialmente el rendimiento.

  • Move constructor no siempre se puede aplicar sin modificaciones.

    Algunas clases siempre tienen sus punteros asignados y, por lo tanto, tales clases siempre eliminan sus punteros en el destructor. En estos casos, deberá agregar controles adicionales para determinar si sus punteros están asignados o se han retirado (ahora son nulos).




Básicamente, es así: si no declara ninguna operación de movimiento, debe respetar la regla de tres. Si declara una operación de movimiento, no hay ningún daño en "violar" la regla de tres, ya que la generación de operaciones generadas por el compilador se ha vuelto muy restrictiva. Incluso si no declara operaciones de movimiento y viola la regla de tres, se espera que un compilador C ++ 0x le avise en caso de que una función especial haya sido declarada por el usuario y otras funciones especiales se hayan generado automáticamente debido a un ahora en desuso "regla de compatibilidad C ++ 03".

Creo que es seguro decir que esta regla se vuelve un poco menos significativa. El verdadero problema en C ++ 03 es que la implementación de diferentes semánticas de copia requiere que el usuario declare todas las funciones especiales relacionadas para que ninguna de ellas sea generada por el compilador (que de lo contrario haría lo incorrecto). Pero C ++ 0x cambia las reglas sobre la generación de funciones de miembros especiales. Si el usuario declara solo una de estas funciones para cambiar la semántica de la copia, evitará que el compilador genere automáticamente las funciones especiales restantes. Esto es bueno porque una declaración faltante convierte un error de tiempo de ejecución en un error de compilación ahora (o al menos una advertencia). Como medida de compatibilidad con C ++ 03, todavía se generan algunas operaciones, pero esta generación se considera obsoleta y al menos debería producir una advertencia en el modo C ++ 0x.

Debido a las reglas bastante restrictivas sobre las funciones especiales generadas por el compilador y la compatibilidad con C ++ 03, la regla de tres sigue siendo la regla de tres.

Aquí hay algunos ejemplos que deberían estar bien con las nuevas reglas de C ++ 0x:

template<class T>
class unique_ptr
{
   T* ptr;
public:
   explicit unique_ptr(T* p=0) : ptr(p) {}
   ~unique_ptr();
   unique_ptr(unique_ptr&&);
   unique_ptr& operator=(unique_ptr&&);
};

En el ejemplo anterior, no es necesario declarar ninguna de las otras funciones especiales como eliminadas. Simplemente no se generarán debido a las reglas restrictivas. La presencia de operaciones de movimiento declaradas por el usuario deshabilita las operaciones de copia generadas por el compilador. Pero en un caso como este:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
};

ahora se espera que un compilador C ++ 0x produzca una advertencia sobre las operaciones de copia posiblemente generadas por el compilador que podrían hacer algo incorrecto. Aquí, la regla de los tres asuntos y debe ser respetada. Una advertencia en este caso es totalmente apropiada y le da al usuario la oportunidad de manejar el error. Podemos deshacernos del problema a través de funciones eliminadas:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
   scoped_ptr(scoped_ptr const&) = delete;
   scoped_ptr& operator=(scoped_ptr const&) = delete;
};

Por lo tanto, la regla de tres aún se aplica aquí simplemente debido a la compatibilidad con C ++ 03.




En el caso general, entonces sí, la regla de tres se convirtió en la de cinco, con el operador de asignación de movimientos y el constructor de movimientos agregados. Sin embargo, no todas las clases son copiables y movibles, algunas son movibles, algunas son simplemente copiables.




Related