2009-02-13 13 views
8

Considere estos tipos:¿Se puede configurar la "optimización de base vacía" en GCC?

struct A {}; 
    struct B : A { int i; }; 

sizeof(A) > 0 como lo exige la norma.

sizeof(B) debe ser 4 debido a la optimización de base vacía. Sin embargo, en GCC 4.1.1 es 5 (estoy usando un paquete de 1 en esta área). Y de forma inconsistente: algunos de mis archivos lo están obteniendo, otros no. No puedo estar seguro de cuáles son las diferencias todavía, tenemos un gran impacto.

En los otros tres compiladores que estoy usando (por Microsoft y Freescale), no tengo este problema. La optimización de la base vacía es opcional, aparentemente, de acuerdo con this article.

¿Hay una opción de compilador o pragma para sintonizar esto en GCC 4.1.1? Puedo solucionar el problema, pero me gustaría entender qué está pasando primero. Busqué en Google por un tiempo y parece que no puedo encontrar nada.

Respuesta

10

Esto siempre sucede. Publico inmediatamente antes de resolverlo. Tal vez el acto de publicar me hace pensar de otra manera ...

Así que en mi pregunta la muestra fue un poco simplificada en exceso. En realidad es más como esto:

struct Base {}; 
struct C1 : Base { int i; } 
struct C2 : Base { C1 c; int i; } 

sizeof (C1) es correctamente 4 en todas las plataformas, pero sizeof (C2) es 9 en lugar de 8 en la GCC. Y ... al parecer, GCC es lo único que hace las cosas bien, de acuerdo con el último fragmento del artículo al que me he vinculado en la pregunta original. Lo citaré (de Nathan Meyers) aquí:

Toda una familia de optimizaciones relacionadas de "subobjetos vacíos" son posibles, sujetas a las especificaciones ABI que debe observar el compilador. (Jason Merrill me señaló algunos de estos, hace algunos años). Por ejemplo, considere tres miembros de estructura de tipos (vacíos) A, B y C, y un cuarto que no esté vacío. Es posible que, de manera uniforme, todos ocupen la misma dirección, siempre que no tengan ninguna base en común entre ellos o con la clase que los contiene. Un problema común en la práctica es tener el primer (o único) miembro de una clase derivado de la misma base vacía que la clase. El compilador tiene que insertar relleno para que los dos subobjetos tengan direcciones diferentes. Esto ocurre realmente en adaptadores de iterador que tienen un miembro interator, ambos derivados de std :: iterator. Un estándar implícitamente implementado std :: reverse_iterator puede presentar este problema.

Por lo tanto, la incoherencia que estaba viendo era solo en los casos en que tenía el patrón anterior. Cada otro lugar que estaba derivando de una estructura vacía estaba bien.

Bastante fácil de evitar. Gracias a todos por los comentarios y respuestas.

+1

Compilador de Intel lo hace bien, así como GCC. MSVC es demasiado activo y viola los estándares en la optimización de base vacía cuando una de las clases base también es una clase base del primer miembro. He publicado reglas mucho más exhaustivas para las reglas de base vacía de VC++ (2005/2008) y GCC (4.1.1). http://stackoverflow.com/questions/547290/is-the-empty-base-optimization-in-gcc-configurable/919694#919694 – Adisak

0

Bueno, no es una respuesta completa, pero el GCC Manual menciona que g ++ a veces puede colocar las clases base vacías en el desplazamiento incorrecto. Vea el bit sobre la opción -Wabi.

1

GCC C++ sigue estas reglas con relleno estándar:

NOTA: __attribute__((__packed__)) o cambiar el embalaje del defecto será modificar estas reglas.

  • clase EmptyBase {}; -> sizeof (EmptyBase) == 1

  • Cualquier cantidad de bases vacías se correlacionará con 0 en el desplazamiento de estructura, siempre que todos sean tipos únicos (incluida la crianza).

  • Los padres sin base vacía están simplemente en el orden declarado con solo relleno para la alineación.

  • Si el primer miembro de una clase derivada que sigue inmediatamente bases vacías no se deriva de ninguna de esas bases, se puede comenzar en la primera compensación correctamente alineada para ese miembro que es mayor que o igual a la dirección de base vacía; esta puede ser la misma dirección que las bases vacías.

  • Si el primer miembro de una clase derivada que sigue inmediatamente bases vacías deriva de cualquiera de esas bases, comenzará en la primera compensación correctamente alineada para ese miembro que sea mayor que la dirección de base vacía, - Esta nunca es la misma dirección que las bases vacías.

  • Los miembros que son clases vacías toman al menos un byte de almacenamiento en la clase contenedora.


MSVC++ sigue estas reglas:

NOTA: #pragma pack o cambiar el embalaje del defecto será modificar estas reglas.

  • clase EmptyBase {}; -> sizeof (EmptyBase) == 1

  • La única forma en que una clase de base vacía (o clase derivada de una base vacía) comenzará en el desplazamiento 0 (cero) es si es la primera clase base.

  • Una clase de base no vacía comenzará en la siguiente compensación de alineación válida para la clase base.

  • Todas las clases base vacías parecerán tener cero almacenamiento efectivo en la clase derivada y no afectan el desplazamiento actual a menos que sea seguido por otra clase base vacía (o clase derivada de una base vacía) en cuyo caso debería ver la siguiente regla.

  • Una clase de base vacía (o clase derivada de una base vacía) que sigue una clase de base vacía (o clase derivada de una base vacía) agregará 1 a la posición de desplazamiento actual antes de rellenar el alineación para la clase.

  • No hay relleno (aparte de la alineación) entre la última clase base y el miembro de la primera clase o puntero (s) de vft. *** NOTA: esta es una optimización de base vacía demasiado agresiva que puede romper el estándar de C++.

  • Los miembros que son clases vacías toman al menos un byte de almacenamiento en la clase contenedora.

Cuestiones relacionadas