static_cast reinterpret_cast ejemplos c++ c++11 memcpy reinterpret-cast

c++ - reinterpret_cast - ¿Es el bit std:: array compatible con la matriz C antigua?



reinterpret_cast c++ ejemplos (5)

¿Es la representación de bits subyacente para un std::array<T,N> v y una T u[N] la misma?

En otras palabras, ¿es seguro copiar N*sizeof(T) bytes de uno a otro? (Ya sea a través de reinterpret_cast o memcpy .)

Editar:

Para aclarar, el énfasis está en la misma representación de bits y reinterpret_cast .

Por ejemplo, supongamos que tengo estas dos clases sobre algún tipo T copiable trivialmente, para algunos N :

struct VecNew { std::array<T,N> v; }; struct VecOld { T v[N]; };

Y ahí está la función legada.

T foo(const VecOld& x);

Si las representaciones son las mismas, entonces esta llamada es segura y evita copiar:

VecNew x; foo(reinterpret_cast<const VecOld&>(x));


El requisito del método data() es que devuelva un puntero T* tal que:

[data(), data() + size()) es un rango válido, y data() == addressof(front()) .

Esto implica que puede acceder a cada elemento de forma secuencial a través del puntero de data() , por lo que si T se puede copiar de forma trivial, puede usar memcpy para copiar sizeof(T) * size() bytes a / desde una matriz T[size()] , ya que esto es equivalente a memcpy cada elemento individualmente.

Sin embargo, no puede usar reinterpret_cast , ya que eso violaría un alias estricto, ya que no es obligatorio que data() esté respaldado por una matriz, y también, incluso si tuviera que garantizar que std::array contiene una matriz, ya que C + +17 no puedes (incluso usando reinterpret_cast ) lanzar un puntero a una matriz a / desde un puntero a su primer miembro (tienes que usar std::launder ).


Esto no responde directamente a su pregunta, pero simplemente debe usar std::copy :

T c[N]; std::array<T, N> cpp; // from C to C++ std::copy(std::begin(c), std::end(c), std::begin(cpp)); // from C++ to C std::copy(std::begin(cpp), std::end(cpp), std::begin(c));

Si T es un tipo que se puede copiar de forma trivial, esto se compilará en memcpy . Si no lo es, entonces esto hará una asignación de copia por elementos y será correcto. De cualquier manera, esto hace lo correcto y es bastante legible. No es necesaria la aritmética manual de bytes.


Yo digo que sí (pero la norma no lo garantiza).

Según [array] / 2:

Una matriz es un agregado ([ dcl.init.aggr ]) que se puede inicializar en lista con hasta N elementos cuyos tipos son convertibles a T.

Y [dcl.init.aggr]:

Un agregado es una matriz o una clase (Cláusula [clase]) con

  • no hay constructores proporcionados, explícitos o heredados por el usuario ([class.ctor]),

  • no miembros privados o protegidos de datos no estáticos (Cláusula [class.access]),

  • sin funciones virtuales ([class.virtual]), y

  • no hay clases básicas virtuales, privadas o protegidas ([class.mi]).

A la luz de esto, "se puede inicializar la lista" solo es posible si no hay otros miembros al comienzo de la clase y no hay vtable.

Entonces, los data() se especifican como:

constexpr T* data() noexcept;

Devuelve : Un puntero tal que [data(), data() + size()) es un rango válido, y data() == addressof(front()) .

El estándar básicamente quiere decir "devuelve una matriz", pero deja la puerta abierta para otras implementaciones.

La única otra implementación posible es una estructura con elementos individuales, en cuyo caso puede tener problemas de aliasing. Pero en mi opinión, este enfoque no añade nada más que complejidad. No hay nada que ganar al desenrollar una matriz en una estructura.

Por lo tanto, no tiene sentido no implementar std::array como una matriz.

Pero existe una laguna.


array no exige mucho sobre el tipo subyacente sobre el que se crea una instancia.

Para tener la posibilidad de obtener resultados útiles del uso de memcpy o reinterpret_cast para hacer una copia, el tipo de instancia sobre el que se haya creado una instancia tendría que ser trivialmente copiable. El almacenamiento de esos elementos en una array no afecta el requisito de que memcpy (y memcpy ) solo funcione con tipos trivialmente copiables.

Se requiere que la array sea ​​un contenedor contiguo y un agregado, lo que significa que el almacenamiento de los elementos debe ser una matriz. El estándar lo muestra como:

T elems[N]; // exposition only

Más tarde, sin embargo, tiene una nota que al menos implica que se requiere una matriz (§ [array.overview] / 4):

[Nota: los elementos de la variable miembro se muestran solo para la exposición, para enfatizar que la array es un agregado de clase. El nombre elems no es parte de la interfaz de la matriz. "Nota final"

[énfasis añadido]

Tenga en cuenta que solo el nombre específico no es necesario.


std::array proporciona data() método data() que se pueden usar para copiar a / desde una matriz de estilo C del tamaño adecuado:

const size_t size = 123; int carray[size]; std::array<int,size> array; memcpy( carray, array.data(), sizeof(int) * size ); memcpy( array.data(), carray, sizeof(int) * size );

Como se indica en la documentation

Este contenedor es un tipo agregado con la misma semántica que una estructura que contiene una matriz de estilo C T [N] como su único miembro de datos no estáticos.

por lo que parece que la huella de memoria sería compatible con la matriz de estilo c, aunque no está claro por qué quiere usar "hacks" con reinterpret_cast cuando hay una forma adecuada que no tiene ningún costo.