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.