Es importante darse cuenta de que el código que produce el compilador no tiene conocimiento real de sus estructuras de datos (porque tal cosa no existe en el nivel de ensamblaje), y tampoco el optimizador. El compilador solo produce código para cada función , no estructuras de datos .
Ok, también escribe secciones de datos constantes y demás.
En base a eso, ya podemos decir que el optimizador no "eliminará" ni "eliminará" miembros, porque no genera estructuras de datos. Genera código , que puede o no usar a los miembros, y entre sus objetivos está el ahorro de memoria o ciclos al eliminar usos sin sentido (es decir, escrituras / lecturas) de los miembros.
La esencia de esto es que "si el compilador puede probar dentro del alcance de una función (incluidas las funciones que estaban integradas en ella) que el miembro no utilizado no hace ninguna diferencia en cómo opera la función (y lo que devuelve), entonces es muy probable que la presencia del miembro no provoca gastos generales ".
A medida que hace que las interacciones de una función con el mundo exterior sean más complicadas / poco claras para el compilador (tome / devuelva estructuras de datos más complejas, por ejemplo, un std::vector<Foo>
, , ocultar la definición de una función en una unidad de compilación diferente, prohibir / desincentivar la inserción, etc.) , es cada vez más probable que el compilador no pueda probar que el miembro no utilizado no tiene ningún efecto.
No hay reglas estrictas aquí porque todo depende de las optimizaciones que realice el compilador, pero siempre que haga cosas triviales (como se muestra en la respuesta de YSC) es muy probable que no haya sobrecarga, mientras que hace cosas complicadas (por ejemplo, regresar a std::vector<Foo>
de una función demasiado grande para insertar) probablemente incurrirá en gastos generales.
Para ilustrar el punto, considere este ejemplo :
struct Foo {
int var1 = 3;
int var2 = 4;
int var3 = 5;
};
int test()
{
Foo foo;
std::array<char, sizeof(Foo)> arr;
std::memcpy(&arr, &foo, sizeof(Foo));
return arr[0] + arr[4];
}
Aquí hacemos cosas no triviales (tomar direcciones, inspeccionar y agregar bytes de la representación de bytes ) y, sin embargo, el optimizador puede darse cuenta de que el resultado es siempre el mismo en esta plataforma:
test(): # @test()
mov eax, 7
ret
No solo los miembros de Foo
no ocuparon ningún recuerdo, ¡ Foo
ni siquiera llegaron a existir! Si hay otros usos que no se pueden optimizar, por ejemplo, sizeof(Foo)
podría ser importante, ¡pero solo para ese segmento de código! Si todos los usos pudieran optimizarse así, entonces la existencia de, por ejemplo var3
, no influye en el código generado. Pero incluso si se usa en otro lugar, ¡ test()
permanecerá optimizado!
En resumen: cada uso de Foo
se optimiza de forma independiente. Algunos pueden usar más memoria debido a un miembro innecesario, otros pueden no. Consulte el manual del compilador para obtener más detalles.