c++ c++11 standards volatile typetraits

c++ - std:: is_trivially_copyable-¿Por qué los tipos escalares volátiles no se pueden copiar de forma trivial?



c++11 standards (1)

Aparentemente es la forma en que se reparó un defecto en el estándar, pero no eres el único confundido al respecto.

De http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2094 :

  1. Constructor trivial de copiar / mover para clase con miembro volátil

Sección: 12.8 [class.copy] Estado: abierto Remitente: Daveed Vandevoorde Fecha: 2015-03-06

La resolución del problema 496 incluyó la adición del párrafo 12.2 [class.copy] 12.8, lo que hace que el constructor de copia / movimiento de una clase no sea trivial si tiene un miembro de datos no estáticos de tipo calificado por volatilidad. Este cambio rompe el IA-64 ABI, por lo que se ha solicitado que CWG reconsidere este aspecto de la resolución.

En una nota relacionada, la resolución del problema 496 también modificó el párrafo 9 [tipos básicos] 3.9 [3], lo que hace que los tipos escalares calificados para la volatilidad sean "triviales" pero no "copiables trivialmente". No está claro por qué se hace aquí una distinción; el único uso real de "tipo trivial" en la Norma parece estar en la descripción de qsort, que probablemente debería usar "copiable de forma trivial". (Consulte también el número 1746).

A partir de la descripción del ejemplar (desde el 30.12.2004):

  1. ¿Es un tipo calificado volátil realmente un POD? :

Sin embargo, en 3.9 [basic.types] párrafo 3, el estándar deja claro que los POD se pueden copiar "como si" fueran una colección de bytes por memcpy:

Para cualquier tipo de POD T, si dos punteros a T apuntan a objetos T distintos obj1 y obj2, donde ni obj1 ni obj2 es un subobjeto de clase base, si el valor de obj1 se copia en obj2, usando la función de biblioteca std :: memcpy , obj2 subsecuentemente tendrá el mismo valor que obj1. El problema con esto es que un tipo calificado volátil puede necesitar ser copiado de una manera específica (por ejemplo, copiando usando solo operaciones atómicas en plataformas multiproceso) para evitar el "desgarre de memoria" que puede ocurrir con un byte-by -byte copia.

Me doy cuenta de que la norma dice muy poco acerca de los tipos calificados volátiles y nada (todavía) acerca de las plataformas multiproceso, pero no obstante, este es un problema real, por la siguiente razón:

El próximo TR1 definirá una serie de rasgos que brindan información sobre las propiedades de un tipo, incluido si un tipo es un POD y / o tiene operaciones triviales de construcción / copia / asignación. Las bibliotecas pueden usar esta información para optimizar su código según corresponda, por ejemplo, una matriz de tipo T podría copiarse con una copia de memcpy en lugar de una de elemento por elemento si T es un POD. Esta fue una de las principales motivaciones detrás del capítulo de rasgos de tipo de la TR1. Sin embargo, no está claro cómo deben manejarse los tipos volátiles (o los POD que tienen un tipo volátil como miembro) en estos casos. Notas de la reunión de abril de 2005:

No está claro si el calificador volátil realmente garantiza la atomicidad de esta manera. Además, en este punto, es probable que el trabajo sobre el modelo de memoria para los subprocesos múltiples realizado por el Grupo de Trabajo de Evolución especifique semánticas adicionales para datos volátiles, y ese trabajo debería considerarse antes de resolver este problema.

Los estándares actuales para C ++ 17 (y he observado una redacción similar para C ++ 11) tienen una redacción muy confusa para tipos trivialmente copiables. Primero me topé con este problema con el siguiente código (GCC 5.3.0):

class TrivialClass {}; std::is_trivially_copyable<int volatile>::value; // 0 std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??

Para empeorar aún más la confusión, traté de comprobar lo que std::is_trivial tenía que decir sobre el asunto, solo para que la confusión fuera mayor.

class TrivialClass {}; std::is_trivial<int volatile>::value; // 1 ?? std::is_trivial<TrivialClass volatile>::value; // 1

Confundido, revisé el último borrador de C ++ 17 para ver si algo estaba mal, y encontré algunas palabras ligeramente ambiguas que podrían ser las culpables:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73

Los tipos escalares no calificados cv, los tipos de clase que se pueden copiar de forma trivial (Cláusula 9), las matrices de dichos tipos y las versiones no calificadas por constancia de estos tipos (3.9.3) se denominan colectivamente tipos de copia trivial.

Aquí está la información sobre las clases copiables trivialmente:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.226

Una clase trivialmente copiable es una clase que:

- (6.1) no tiene constructores de copia no triviales (12.8),

- (6.2) no tiene constructores de movimientos no triviales (12.8),

- (6.3) no tiene operadores de asignación de copia no triviales (13.5.3, 12.8),

- (6.4) no tiene operadores de asignación de movimientos no triviales (13.5.3, 12.8), y

- (6.5) tiene un destructor trivial (12.4).

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#section.12.8

Constructores:

Un constructor de copiar / mover para la clase X es trivial si no es proporcionado por el usuario, su lista de tipos de parámetros es equivalente a la lista de tipos de parámetros de una declaración implícita, y si

- (12.1) la clase X no tiene funciones virtuales (10.3) ni clases base virtuales (10.1), y

- (12.2) la clase X no tiene miembros de datos no estáticos de tipo calificado volátil , y

- (12.3) el constructor seleccionado para copiar / mover cada subobjeto de clase base directa es trivial, y

- (12.4) para cada miembro de datos no estáticos de X que es de tipo de clase (o matriz de ellos), el constructor seleccionado para copiar / mover ese miembro es trivial;

de lo contrario, el constructor de copiar / mover no es trivial.

Asignación:

Un operador de asignación de copia / movimiento para la clase X es trivial si no es proporcionado por el usuario, su lista de tipos de parámetros es equivalente a la lista de tipos de parámetros de una declaración implícita, y si

- (25.1) la clase X no tiene funciones virtuales (10.3) ni clases base virtuales (10.1), y

- (25.2) la clase X no tiene miembros de datos no estáticos de tipo calificado volátil , y

- (25.3) el operador de asignación seleccionado para copiar / mover cada subobjeto de clase base directa es trivial, y

- (25.4) para cada miembro de datos no estáticos de X que es de tipo de clase (o conjunto de ellos), el operador de asignación seleccionado para copiar / mover ese miembro es trivial;

de lo contrario, el operador de asignación de copia / movimiento no es trivial.

Nota: Actualizada esta sección con más información. Ahora creo que esto es un error en GCC. Sin embargo, esto solo no responde a todas mis preguntas.

Pude ver que tal vez sea porque TrivialClass no tiene miembros no estáticos, ya que eso pasaría las reglas anteriores, así que agregué un int, y aún se devuelve como copiable de forma trivial.

class TrivialClass { int foo; }; std::is_trivially_copyable<int volatile>::value; // 0 std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??

El estándar establece que la volatilidad debe ser heredada por subobjetos de un objeto volátil. Lo que significa que el miembro de datos no estáticos de TrivialClass volatile ahora debe ser de tipo int volatile .

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.76

Un objeto volátil es un objeto de tipo volátil T, un subobjeto de tal objeto o un subobjeto mutable de un objeto volátil const.

Podemos confirmar que esto está funcionando en GCC a través de:

std::is_same<decltype(((TrivialClass volatile*)nullptr)->foo), int volatile>::value; // 1! (Expected)

Confundido, luego agregué un volátil al propio int foo . Todavía pasa, lo que obviamente es un error!

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68905#c1

class TrivialClass { int volatile foo; }; std::is_trivially_copyable<int volatile>::value; // 0 std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??

Continuando, vemos que std::is_trivial también funciona como se esperaba:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73

Los tipos escalares, los tipos de clases triviales (Cláusula 9), las matrices de dichos tipos y las versiones calificadas para CV de estos tipos (3.9.3) se denominan colectivamente tipos triviales.

Bien, tengo muchas preguntas aquí.

  • ¿Por qué la materia volátil es is_trivially_copyable y no is_trivial?
  • ¿Cuál es el problema con is_trivially_copyable y los tipos de objetos, es un error o un problema con el estándar?
  • ¿Por qué importa si algo es volátil de todos modos?

¿Alguien puede ayudarme a envolver mi cabeza en torno a esto? Estoy realmente perdido aquí.