how headers files custom c++ c arrays struct language-lawyer

headers - Aliasing struct y array the C++ way



how to use.h in c (4)

En mi humilde opinión, la forma más fácil es simplemente implementar el operator[] . Puedes hacer una matriz de ayuda como esta o simplemente crear un interruptor ...

struct Point { double const& operator[] (std::size_t i) const { const std::array coords {&x, &y, &z}; return *coords[i]; } double& operator[] (std::size_t i) { const std::array coords {&x, &y, &z}; return *coords[i]; } double x; double y; double z; }; int main() { Point p {1, 2, 3}; std::cout << p[2] - p[1]; return 0; }

Este es un seguimiento en C ++ para otra pregunta mía

En los viejos tiempos de la pre-ISO C, el siguiente código no habría sorprendido a nadie:

struct Point { double x; double y; double z; }; double dist(struct Point *p1, struct Point *p2) { double d2 = 0; double *coord1 = &p1->x; double *coord2 = &p2->x; int i; for (i=0; i<3; i++) { double d = coord2[i] - coord1[i]; // THE problem d2 += d * d; } return sqrt(d2); }

Desafortunadamente, esta línea problemática usa la aritmética del puntero ( p[i] siendo por definición *(p + i)) fuera de cualquier matriz que explícitamente no está permitida por el estándar. El borrador 4659 para C ++ 17 dice en 8.7 [expr.add]:

Si la expresión P apunta al elemento x [i] de un objeto de matriz x con n elementos, las expresiones P + J y J + P (donde J tiene el valor j) apuntan al elemento (posiblemente hipotético) x [i + j] si 0 <= i + j <= n; de lo contrario, el comportamiento no está definido.

Y la nota (no normativa) 86 lo hace aún más explícito:

Se considera que un objeto que no es un elemento de matriz pertenece a una matriz de un solo elemento para este propósito. Un puntero pasado el último elemento de una matriz x de n elementos se considera equivalente a un puntero a un elemento hipotético x [n] para este fin.

La respuesta aceptada de la pregunta a la que se hace referencia utiliza el hecho de que el lenguaje C acepta los juegos de palabras a través de los sindicatos, pero nunca pude encontrar el equivalente en el estándar C ++. Así que supongo que una unión que contenga un miembro de estructura anónima y una matriz conduciría a un Undefined Behaviour en C ++, son idiomas diferentes ...

Pregunta:

¿Cuál podría ser una forma condicional de iterar a través de los miembros de una estructura como si fueran miembros de una matriz en C ++? Estoy buscando un camino en las versiones actuales (C ++ 17), pero las soluciones para versiones anteriores también son bienvenidas.

Renuncia:

Obviamente, solo se aplica a elementos del mismo tipo, y el relleno se puede detectar con una assert simple como se muestra en esa otra question , por lo que el relleno, la alineación y los tipos mixtos no son mi problema aquí.


Puede usar el hecho de que al lanzar un puntero a intptr_t haciendo aritmética y luego devolver el valor al tipo de puntero se implementa un comportamiento definido . Creo que funcionará en la mayoría de los compiladores:

template<class T> T* increment_pointer(T* a){ return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(a)+sizeof(T)); }

Esta técnica es la más eficiente, los optimizadores parecen no ser capaces de producir óptimos si se busca una tabla de un solo uso: assemblies-comparison


Use un conjunto de constexpr de puntero a miembro:

#include <math.h> struct Point { double x; double y; double z; }; double dist(struct Point *p1, struct Point *p2) { constexpr double Point::* coords[3] = {&Point::x, &Point::y, &Point::z}; double d2 = 0; for (int i=0; i<3; i++) { double d = p1->*coords[i] - p2->*coords[i]; d2 += d * d; } return sqrt(d2); }


struct Point { double x; double y; double z; double& operator[]( std::size_t i ) { auto self = reinterpret_cast<uintptr_t>( this ); auto v = self+i*sizeof(double); return *reinterpret_cast<double*>(v); } double const& operator[]( std::size_t i ) const { auto self = reinterpret_cast<uintptr_t>( this ); auto v = self+i*sizeof(double); return *reinterpret_cast<double const*>(v); } };

esto se basa en que no hay empaquetamiento entre las double en su `struct. Afirmar eso es difícil.

Una estructura POD es una secuencia de bytes garantizados.

Un compilador debería ser capaz de compilar [] hasta las mismas instrucciones (o la falta de ellas) como un acceso de matriz sin formato o una aritmética de puntero. Puede haber algunos problemas donde esta optimización ocurre "demasiado tarde" para que ocurran otras optimizaciones, por lo tanto, vuelva a verificar el código sensible al rendimiento.

Es posible que la conversión a char* o std::byte* uintptr_t de uintptr_t sea ​​válida, pero hay un problema central acerca de si la aritmética del puntero está permitida en este caso.