¿Qué significa "predeterminado" después de una declaración de función de clase?

221

Lo he visto defaultjunto a las declaraciones de funciones en una clase. ¿Qué hace?

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};
Paul Manta
fuente
26
¿Qué hace el "&" que precede al "=" en las declaraciones del operador de asignación?
dshin
66
@dshin Esta es una nueva calificación de una función miembro .
Kane

Respuestas:

249

Es una nueva característica de C ++ 11 .

Significa que desea utilizar la versión generada por el compilador de esa función, por lo que no necesita especificar un cuerpo.

También puede usar = deletepara especificar que no desea que el compilador genere esa función automáticamente.

Con la introducción de constructores de movimiento y operadores de asignación de movimiento, las reglas para cuando se generan versiones automáticas de constructores, destructores y operadores de asignación se han vuelto bastante complejas. Usar = defaulty = deletefacilita las cosas ya que no necesita recordar las reglas: solo dice lo que quiere que suceda.

Peter Alexander
fuente
17
= deletees más fuerte: significa que está prohibido usar esa función, aunque todavía participa en la resolución de sobrecarga.
Deduplicador
2
Pero, si queremos usar la definición de generación del compilador, ¿no deberíamos omitir la escritura de esa función en lugar de "primero escribirla y luego asignarla a la predeterminada"?
Mayank Jindal
47

Esta es una nueva característica de C ++ 0x que le dice al compilador que cree la versión predeterminada del constructor u operador de asignación respectivo, es decir, la que solo realiza la acción de copiar o mover para cada miembro. Esto es útil porque el constructor de movimiento no siempre se genera de manera predeterminada (por ejemplo, si tiene un destructor personalizado), a diferencia del constructor de copia (y de la misma manera para la asignación), pero si no hay nada no trivial para escribir, es mejor dejar que el compilador lo maneja que deletrearlo cada vez.

Observe también que no se generaría un constructor predeterminado si proporciona cualquier otro constructor no predeterminado. Si todavía desea el constructor predeterminado, también, puede usar esta sintaxis para que el compilador haga uno.

Como otro caso de uso, hay varias situaciones en las que un constructor de copia no se generaría implícitamente (por ejemplo, si proporciona un constructor de movimiento personalizado). Si aún desea la versión predeterminada, puede solicitarla con esta sintaxis.

Vea la Sección 12.8 de la norma para más detalles.

Kerrek SB
fuente
55
A pesar de que no es sólo para los constructores y tareas, pero también se aplica a operator new/new[], operator delete/delete[]y sus sobrecargas.
Sebastian Mach
21

Es nuevo en C ++ 11, ver aquí . Puede ser bastante útil si ha definido un constructor, pero desea utilizar los valores predeterminados para los demás. Antes de C ++ 11, tendría que definir todos los constructores una vez que haya definido uno, incluso si son equivalentes a los valores predeterminados.

También tenga en cuenta que en ciertas situaciones es imposible proporcionar un constructor predeterminado definido por el usuario que se comporte de la misma manera que el compilador sintetizado bajo la inicialización predeterminada y de valor . defaultle permite recuperar ese comportamiento.

juanchopanza
fuente
55
con respecto al segundo párrafo, ¿puedes dar un ejemplo?
John Smith
11

Otro caso de uso que no veo mencionado en estas respuestas es que fácilmente le permite cambiar la visibilidad de un constructor. Por ejemplo, tal vez desee que una clase de amigo pueda acceder al constructor de copias, pero no desea que esté disponible públicamente.

dshin
fuente
1

C ++ 17 N4659 borrador estándar

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 11.4.2 "Funciones explícitamente predeterminadas":

1 Una definición de función de la forma:

attribute-specifier-seq opt decl-specifier-seq opt declarator virt-specifier-seq opt = default ;

se llama una definición explícitamente predeterminada. Una función que está explícitamente predeterminada

  • (1.1) - ser una función miembro especial,

  • (1.2): tienen el mismo tipo de función declarada (excepto para posibles calificadores de referencia diferentes y excepto que en el caso de un constructor de copia u operador de asignación de copia, el tipo de parámetro puede ser "referencia a T no constante", donde T es el nombre de la clase de la función miembro) como si se hubiera declarado implícitamente, y

  • (1.3): no tiene argumentos predeterminados.

2 Una función explícitamente predeterminada que no se define como eliminada puede declararse constexpr solo si se hubiera declarado implícitamente como constexpr. Si una función se omite explícitamente en su primera declaración, se considera implícitamente constexpr si la declaración implícita lo fuera.

3 Si una función que está explícitamente predeterminada se declara con un especificador noexcept que no produce la misma especificación de excepción que la declaración implícita (18.4), entonces

  • (3.1) - si la función está explícitamente predeterminada en su primera declaración, se define como eliminada;

  • (3.2) - de lo contrario, el programa está mal formado.

4 [Ejemplo:

struct S {
  constexpr S() = default;            // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;             // ill-formed: default argument
  void operator=(const S&) = default; // ill-formed: non-matching return type
  ~ S() noexcept(false) = default;    // deleted: exception specification does not match
private:
  int i;                              // OK: private copy constructor
  S(S&);
};
S::S(S&) = default;                   // OK: defines copy constructor

- ejemplo final]

5 Las funciones predeterminadas explícitamente y las funciones declaradas implícitamente se denominan colectivamente funciones predeterminadas, y la implementación proporcionará definiciones implícitas para ellas (15.1 15.4, 15.8), lo que podría significar definirlas como eliminadas. Una función es proporcionada por el usuario si es declarada por el usuario y no está explícitamente predeterminada o eliminada en su primera declaración. Una función explícitamente predeterminada por el usuario (es decir, explícitamente predeterminada después de su primera declaración) se define en el punto donde está explícitamente predeterminada; si dicha función se define implícitamente como eliminada, el programa está mal formado. [Nota: Declarar una función como predeterminada después de su primera declaración puede proporcionar una ejecución eficiente y una definición concisa al tiempo que permite una interfaz binaria estable a una base de código en evolución. - nota final]

6 [Ejemplo:

struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~ trivial() = default;
};
struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;       // not first declaration

- ejemplo final]

Entonces, la pregunta es, por supuesto, qué funciones pueden declararse implícitamente y cuándo sucede eso, lo que he explicado en:

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente