tutorial programar para inteligentes inteligente español crear contratos contrato aprender c++ c++11 language-lawyer

c++ - programar - ¿Un constructor predeterminado eliminado aún podría ser trivial?



solidity español (1)

Mirando la definición de constructor por defecto trivial en los estándares:

Un constructor predeterminado es trivial si no es proporcionado por el usuario y si:

  • su clase no tiene funciones virtuales (10.3) ni clases de base virtual (10.1), y
  • ningún miembro de datos no estáticos de su clase tiene un inicializador de corsé o igual, y
  • todas las clases base directas de su clase tienen constructores por defecto triviales, y
  • para todos los miembros de datos no estáticos de su clase que son de tipo de clase (o matriz de ellos), cada clase tiene un constructor predeterminado trivial.

De lo contrario, el constructor predeterminado no es trivial.

Parece que la definición de trivialidad de un constructor predeterminado no descarta la posibilidad de un constructor predeterminado deleted :

struct A { int& a; // the implicitly defaulted default constructor will be defined as deleted }; struct B { B()=delete; // explicitly deleted }; int main() { static_assert(is_trivial<A>::value, ""); static_assert(is_trivial<B>::value, ""); }

El código anterior se ejecuta sin ningún error de aserción. El tipo tiene un constructor predeterminado trivial y se puede copiar de forma trivial, por lo que es una "trivial class" .

¿No haría problemas este tipo de tipo como "trivial class" ? Por ejemplo, para cosas como el tiempo de vida del objeto, la equivalencia de la copia en bytes, la asignación de goto statement, etc.

EDITAR: El siguiente ejemplo de goto permiso no es válido. Gracias por el comentario de @ Casey. Otro ejemplo de equivalencia de copia de byte se agrega para reemplazar este.

Tomemos como ejemplo el permiso para goto declaración, según las normas:

Es posible transferir a un bloque, pero no de una manera que evite las declaraciones con la inicialización. Un programa que salta 87 desde un punto donde una variable con duración de almacenamiento automático no está dentro del alcance hasta un punto donde está dentro del alcance está mal formado a menos que la variable tenga tipo escalar, tipo de clase con un constructor predeterminado trivial y un destructor trivial, un La versión cv calificada de uno de estos tipos, o una matriz de uno de los tipos anteriores y se declara sin un inicializador (8.5).

Así que para el siguiente código:

class A { int& a; public: A(int& aa): a{aa} {} A()=default; // this is necessary otherwise no default constructor will be implicitly declared then the type will not be trivial }; int i; int main() { static_assert(is_trivial<A>::value, ""); goto L; A a{i}; L: return 0; }

Está bien formado de acuerdo con las reglas porque A tiene un constructor predeterminado trivial y un destructor trivial (la afirmación pasa a OK). Por el contrario, el código está mal formado en C ++ 03 (con la sintaxis solo de C ++ 11 eliminada, es decir, la línea de A()=default; ), porque A no es un POD en C ++ 03, y C ++ 03 permite que goto cruce la definición del tipo de POD solamente.

Tomemos como ejemplo la equivalencia de copia en bytes, el estándar dice:

Para cualquier tipo T trivialmente copiable, si dos punteros a T apuntan a objetos T obj1 y obj2 distintos, donde ni obj1 ni obj2 es un subobjeto de clase base, si los bytes subyacentes (1.7) que forman el obj1 se copian en obj2,41 obj2 posteriormente mantendrá el mismo valor que obj1.

Así que memcpy() en el tipo de copia trivial está bien definido:

class A { int& a; public: A(int& aa): a{aa} {} A()=default; // this is necessary otherwise no default constructor will be implicitly declared then the type will not be trivial void* addr() {return &a;} }; int i = 0; int j = 0; int main() { static_assert(is_trivial<A>::value, ""); A a{i}; A b{j}; cout << a.addr() << " " << b.addr() << "/n"; // a = b; // this will be ill-formed because the implicitly defaulted copy assignment is defined as deleted memcpy(&a, &b, sizeof(A)); // this is well-defined because A is trivial cout << a.addr() << " " << b.addr() << "/n"; }

Está bien definido de acuerdo con las reglas porque A es un tipo trivial (la afirmación pasa OK). El resultado muestra que se hace una referencia para referirse a diferentes objetos en diferentes momentos. Por el contrario, el código no está definido en C ++ 03 (con la sintaxis solo de C ++ 11 eliminada, es decir, la línea de A()=default; ), porque A no es un POD en C ++ 03, y C ++ 03 permite la equivalencia de copia en bytes del tipo POD solamente.


El problema 667 del CWG abordó este problema exacto con un cambio que se incorporó en el borrador de trabajo de C ++ cerca de N3225 . N3225 § 12.1 [class.ctor] / 5 estados:

Un constructor predeterminado es trivial si no es proporcionado ni eliminado por el usuario y si:

  • su clase no tiene funciones virtuales (10.3) ni clases de base virtual (10.1), y
  • ningún miembro de datos no estáticos de su clase tiene un inicializador de corsé o igual , y
  • todas las clases base directas de su clase tienen constructores por defecto triviales, y
  • para todos los miembros de datos no estáticos de su clase que son de tipo de clase (o matriz de ellos), cada clase tiene un constructor predeterminado trivial.

De lo contrario, el constructor predeterminado no es trivial .

Esto fue (obviamente) cambiado antes del lanzamiento de C ++ 11. El CWG DR 1135 se creó para abordar un comentario del organismo nacional de Finlandia sobre el borrador del candidato C ++ 11 :

Se debe permitir que se omita explícitamente una función miembro especial no pública en su primera declaración. Es muy probable que los usuarios deseen predeterminar constructores protegidos / privados y copiar constructores sin tener que escribir dichos predeterminados fuera de la clase.

La resolución de este problema eliminó el texto "ni eliminado" de 12.1, así como las secciones que describen los destructores triviales, los constructores triviales de copia / movimiento y los operadores de asignación trivial de copia / movimiento. Creo que este cambio cortó una franja demasiado amplia, y probablemente no fue intencional hacer que tu struct A trivial. De hecho, en el valor nominal es ridículo que este programa esté mal formado:

int x = 42; int y = 13; A a_x{x}; A a_y{y}; a_x = a_y;

pero este programa no lo es, ya que A es trivialmente copiable ( Clang está de acuerdo , GCC no ):

int x = 42; int y = 13; A a_x{x}; A a_y{y}; std::memcpy(&a_x, &a_y, sizeof(a_x));

La existencia del problema de CWG 1496 "Trivialidad con constructores predeterminados eliminados y faltantes" parece indicar que el comité es consciente del problema (o al menos un problema estrechamente relacionado):

Un constructor predeterminado que se define como eliminado es trivial, de acuerdo con el párrafo 12 [class.ctor] 12.1. Esto significa que, de acuerdo con el párrafo 6 [9] de la clase, dicha clase puede ser trivial. Sin embargo, si la clase no tiene un constructor predeterminado porque tiene un constructor declarado por el usuario, la clase no es trivial. Dado que ambos casos impiden la construcción por defecto de la clase, no está claro por qué hay una diferencia en la trivialidad entre los casos.

Aunque todavía no hay resolución.