241 votos

La regla de Tres se convierte en Regla-de-Cinco con C++11?

Así que, después de ver esta maravillosa conferencia sobre r-value referencias, pensé que cada clase se beneficiarían de un "mover " constructor", template<class T> MyClass(T&& other) editar y, por supuesto, una "mudanza operador de asignación", template<class T> MyClass& operator=(T&& other) como Philipp señala en su respuesta que, si se ha asignado dinámicamente miembros, o, en general, las tiendas de los punteros. Al igual que usted debe tener una copia-cto r, operador de asignación y destructor de si los puntos mencionados antes de aplicar. Pensamientos?

231voto

Philipp Puntos 21479

Yo diría que la Regla de Tres se convierte en la Regla de Tres, Cuatro y Cinco:

Cada clase debe definir explícitamente exactamente un de la siguiente conjunto de miembros especiales funciones:

  • Ninguno
  • Destructor, el constructor de copia, de la copia de operador de asignación

Además, cada clase que define explícitamente un destructor puede definir explícitamente un movimiento constructor y/o operador de asignación.

Por lo general, uno de los siguientes conjuntos de miembros especiales funciones es sensata:

  • Ninguno (para muchas clases sencillas donde implícitamente especiales generados por funciones miembro son correctos y rápido)
  • Destructor, el constructor de copia, de la copia de operador de asignación (en este caso el la clase no será móviles)
  • Destructor, mover constructor, mover operador de asignación (en este caso, la clase no se copian, útil para los recursos-gestión de clases en que el subyacente de los recursos no es copiable)
  • Destructor, el constructor de copia, de la copia de operador de asignación, mover constructor (porque de copia elisión, no hay sobrecarga si la copia operador de asignación toma su argumento por valor)
  • Destructor, el constructor de copia, de la copia de operador de asignación, mover constructor, mover operador de asignación

Tenga en cuenta que mover constructor y mover el operador de asignación no ser generado para una clase que declara explícitamente ninguna de las demás funciones miembro, que el constructor de copia y copia operador de asignación no ser generado para una clase que declara explícitamente un movimiento constructor o mover operador de asignación, y que una clase con un explícitamente declarado destructor y definidas implícitamente constructor de copia o implícitamente definido copia operador de asignación es considerado obsoleto. En particular, las siguientes perfectamente válido en C++03 polimórficos de la clase base

class C {
  virtual ~C() { }   // allow subtype polymorphism
};

debe ser reescrita de la siguiente manera:

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

Un poco molesto, pero probablemente mejor que la alternativa (generación automática de todas las funciones miembro).

En contraste con la Regla de los Tres Grandes, donde no se adhieran a la norma puede causar graves daños, no declarar explícitamente el movimiento constructor y mover el operador de asignación es en general bien, pero a menudo subóptima con respecto a la eficiencia. Como se mencionó anteriormente, mover constructor y mover operadores de asignación sólo se generan si no es explícitamente declarado constructor de copia, de la copia de operador de asignación o destructor. Esto no es simétrica a la tradicional de C++03 de comportamiento con respecto a la auto-generación de constructor de copia y copia operador de asignación, pero es mucho más seguro. Así que la posibilidad de definir mover los constructores y mover operadores de asignación es muy útil y crea nuevas posibilidades (puramente muebles clases), pero las clases que se adhieren a la de C++03 Regla de los Tres Grandes todavía va a estar bien.

Los recursos de la gestión de las clases se puede definir el constructor de copia y copia operador de asignación como eliminado (que cuenta como definición) si los recursos que subyacen en el que no pueden copiarse. A menudo, usted todavía desea mover constructor y mover operador de asignación. Copiar y mover los operadores de asignación se suelen aplicarse en el uso de swap, como en C++03. Si usted tiene un movimiento constructor y mover operador de asignación, que se especializa std::swap será importante porque la genérica std::swap utiliza el movimiento constructor y mover operador de asignación, si está disponible, y que debe ser lo suficientemente rápido.

Las clases que no están destinados para la gestión de los recursos (es decir, el no-vacío destructor) o subtipo de polimorfismo (i.e., no destructor virtual) debe declarar que ninguno de los cinco miembros especiales de las funciones; todos ellos serán auto-generados y comportarse correcta y rápida.

41voto

NoSenseEtAl Puntos 2342

puedo creer que nadie vinculado a esto:
http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html Básicamente, el artículo sostiene que "la Regla de Cero". No es la apropiada para mí citar todo el artículo, pero creo que este es el punto principal: (Regla de Cero)

Clases personalizadas destructores, copiar/mover los constructores o copiar/mover asignación >los operadores deben ocuparse exclusivamente de la propiedad. Otras clases no debe tener costumbre destructores, copiar/mover los constructores o copiar/mover operadores de asignación.

También este bit en mi humilde opinión es importante:

Común "de la propiedad-en-un-paquete de" las clases están incluidos en el estándar biblioteca: std::unique_ptr y std::shared_ptr. A través del uso de personalizado deleter objetos, ambos han hecho lo suficientemente flexible como para manejar prácticamente cualquier tipo de recurso.

15voto

Motti Puntos 32921

Yo no lo creo, la regla de tres es una regla general que establece que una clase que implementa uno de los siguientes, pero no de todos ellos es, probablemente, buggy.

  1. Constructor de copia
  2. Operador de asignación
  3. Destructor

Sin embargo deja fuera de la jugada constructor o mover el operador de asignación no implica un error. Esto puede ser una oportunidad perdida en la optimización (en la mayoría de los casos) o que se mueven semántica no son relevantes para esta clase, pero esto no es un error.

Si bien puede ser la mejor práctica para definir un movimiento constructor, cuando sea pertinente, no es obligatorio. Hay muchos casos en los que un movimiento constructor no es relevante para una clase (por ej. std::complex) y todas las clases que se comportan correctamente en C++03 seguirá comportándose correctamente en C++0x, incluso si ellos no definen un movimiento constructor.

13voto

peoro Puntos 12875

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

  • Es sólo una optimización.

    La aplicación de sólo una o dos de las constructor de copia, el operador de asignación o destructor probablemente conducirá a errores, a pesar de no tener un movimiento constructor se acaba de reducir potencialmente el rendimiento.

  • Mover constructor no siempre puede ser aplicado sin modificaciones.

    Algunas clases siempre tienen sus punteros asignado, y por lo tanto dichas clases, siempre borrar sus punteros en el destructor. En estos casos, es necesario añadir controles a decir si sus punteros se asignan o se han alejado (ahora son nulos).

4voto

sellibitze Puntos 13607

Básicamente, es como este: Si no se declara ninguna de las operaciones de mover, se debe respetar la regla de tres. Si se declara una operación de mover, no hay ningún daño en la "violación de la" regla de tres, como la generación de generado por el compilador de operaciones se ha puesto muy restrictivas. Incluso si usted no declarar las operaciones de mover y violan la regla de tres, un compilador de C++0x se espera para darle una advertencia en caso de que una función especial fue el usuario declara y otras funciones especiales se han auto-generado debido a la obsoleta "C++03 de compatibilidad de la regla".

Creo que es seguro decir que esta regla se convierte en un poco menos importante. El verdadero problema en C++03, es que la implementación de diferentes copia semántica requiere que el usuario declara que todos relacionados con funciones especiales para que ninguno de ellos es generado por el compilador (que de otra manera haría mal la cosa). Pero C++0x cambia las reglas acerca de los especiales de función de miembro de la generación. Si el usuario manifiesta sólo una de estas funciones para cambiar la copia de la semántica que va a evitar que el compilador auto-generando el resto de funciones especiales. Esto es bueno porque la falta de una declaración convierte un error en tiempo de ejecución en un error de compilación ahora (o al menos una advertencia). Como C++03 de compatibilidad de la medida de algunas de las operaciones generadas pero esta generación se considera obsoleto y al menos debería producir una advertencia en C++0x modo.

Debido a las normas restrictivas sobre generado por el compilador de funciones especiales, y la de C++03 de compatibilidad, la regla de tres estancias de la regla de tres.

Aquí están algunas exaples que debe estar bien con la nueva C++0x reglas:

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 hay necesidad de declarar cualquiera de las otras funciones especiales como eliminado. Ellos simplemente no se genera debido a las normas restrictivas. La presencia de un usuario declara las operaciones de mover desactiva generado por el compilador las operaciones de copia. 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();
};

un compilador de C++0x ahora se espera producir una advertencia sobre la posibilidad de que el compilador genera de las operaciones de copia que podría hacer lo malo. Aquí, la regla de los tres asuntos y deben ser respetados. Una advertencia en este caso es totalmente adecuado y le da al usuario la oportunidad de manejar el error. Podemos deshacernos de la cuestión a través de la eliminados funciones:

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;
};

Así, la regla de tres que aún se aplica aquí, simplemente, porque C++03 de compatibilidad.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X