2009-08-11 797 views
5

Supongamos que tengo dos clases, con idénticos de dos bibliotecas diferentes:Conversiones entre clases congruentes no relacionadas

namespace A { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

namespace B { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

Cuando intento cruzada de fundición, funcionó:

A::Point3D pa = {3,4,5}; 
B::Point3D* pb = (B::Point3D*)&pa; 
cout << pb->x << " " << pb->y << " " << pb->z << endl; 

¿En qué circunstancias es este garantizado para trabajar? ¿Siempre? Tenga en cuenta que sería altamente indeseable editar una biblioteca externa para agregar un pragma de alineación o algo así. Estoy usando g ++ 4.3.2 en Ubuntu 8.10.

+0

Si tiene pa, qué es lo que necesita pb? Dado que cualquier lugar donde usarías pb podría simplemente tener & pa. No estoy seguro de tener claro el razonamiento ... – ezpz

Respuesta

2

Si las estructuras que está utilizando son simplemente datos y no se utiliza la herencia, creo que siempre debería funcionar.

Siempre y cuando sean POD, debería estar bien. http://en.wikipedia.org/wiki/Plain_old_data_structures

acuerdo con el estándar (1.8.5)

"A menos que se trata de un campo de bits fi (9.6), un objeto más derivada deberá tener un tamaño distinto de cero y deberá ocupar uno o más bytes de almacenamiento. Los subobjetos de clase base pueden tener un tamaño cero. Un objeto de POD5) tipo (3.9) ocupará bytes contiguos de almacenamiento ".

si ocupan bytes contiguos de almacenamiento y que son la misma estructura con diferente nombre, un yeso debe tener éxito

+0

Al leer esa cita del estándar, no descarta el relleno, o garantiza que las dos estructuras usarán un relleno idéntico. Estoy de acuerdo, sin embargo, seguramente funcionará en la práctica. – jalf

+0

Si ambas estructuras se definen de la misma manera y el compilador es el mismo, el relleno debe ser idéntico. –

-2

sé exactamente que no funcionaría:

  • tanto tiene estructura de alineación diferen;
  • compilado con diferentes opciones de RTTI

puede haber alguna otra cosa ...

+0

De http://en.wikipedia.org/wiki/RTTI: "RTTI solo está disponible para las clases que son polimórficas, lo que significa que tienen al menos un método virtual". ¿Su preocupación todavía se aplica a POD? ¿En qué condiciones tendrían una alineación diferente? – Jann

+0

No es así. Hay typeid de palabra clave de C++, que debe devolver el identificador para cualquier tipo (independientemente del uso), por lo que el compilador necesita incrustar un pequeño fragmento en cualquier estructura. Aún más, no sabemos cómo esta estructura será utilizada por el programador final. Entonces, para una sola instancia, esta conversión será correcta, pero para la matriz puede fallar. – Dewfy

+0

el compilador * no * incrusta nada en ninguna estructura. Es fácil verificar esto con sizeof(). Puede crear fácilmente una estructura de tamaño 1. Si crea una estructura vacía, incluso puede tener un tamaño 0 si se utiliza como una clase base. – jalf

0

Esta línea debe ser:

B::Point3D* pb = (B::Point3D*)&pa; 

Nota del &. Creo que lo que estás haciendo es un reinterpret_cast entre dos punteros. De hecho, puede reinterpretar_cast cualquier tipo de puntero a otro, independientemente del tipo de los dos punteros. Pero esto no es seguro, y no es portátil.

Por ejemplo,

int x = 5; 
double* y = reinterpret_cast<double*>(&x); 

sólo se va con la C-estilo, así que la segunda línea es en realidad igual a:

double* z = (double*)&x; 

que odio el C-Style cuando se lanza porque no se puede saber el propósito del reparto de un vistazo :)


¿En qué circunstancias se garantiza que funcione este ?

Esto no es verdadero conversión entre tipos de.Por ejemplo,

int i = 5; 
float* f = reinterpret_cast<float*>(&i); 

Ahora f apunta al mismo lugar que i puntos a. Por lo tanto, no se realiza ninguna conversión. Cuando desreferencia f, obtendrá un flotante con la misma representación binaria del número entero i. Son cuatro bytes en mi máquina.

+0

reinterpret_cast sería una mala idea, ya que técnicamente no está garantizado que resulte en un puntero a la misma dirección. static_cast (static_cast ()) garantiza un puntero a la misma dirección. – jalf

+0

Gracias por la corrección. – Jann

2

Si dos estructuras POD comienzan con la misma secuencia de miembros, la norma garantiza que podrá acceder a ellas libremente a través de una unión. Puede almacenar un A :: Point3D en una unión, y luego leer desde el miembro B :: Point3D, siempre y cuando solo toque los miembros que forman parte de la secuencia común inicial. (De modo que si una estructura contiene int, int, int, float y la otra contenía int, int, int, int, solo se le permitiría acceder a las tres primeras entradas).

Así que parece una forma garantizada de que su código funcione. También significa que el molde debería funcionar, pero no estoy seguro de si esto se establece explícitamente en la norma.

Por supuesto, todo esto supone que ambas estructuras se compilan con el mismo compilador para garantizar un ABI idéntico.

+0

+1: punto excelente. Sería un compilador muy extraño que decidiera tratar POD-struct de manera diferente cuando está anidado en una unión o no. –

0

La siguiente es bastante seguro:

namespace A { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

namespace B { 
    typedef A::Point3D Point3D; 
} 

int main() { 
    A::Point3D a; 
    B::Point3D* b = &a; 

    return 0; 
} 
Cuestiones relacionadas