Il est important de réaliser que le code produit par le compilateur n'a aucune connaissance réelle de vos structures de données (car une telle chose n'existe pas au niveau de l'assembly), et l'optimiseur non plus. Le compilateur produit uniquement du code pour chaque fonction , pas des structures de données .
Ok, il écrit également des sections de données constantes et autres.
Sur cette base, nous pouvons déjà dire que l'optimiseur ne «supprimera» ni «éliminera» les membres, car il ne génère pas de structures de données. Il produit du code , qui peut ou non utiliser les membres, et parmi ses objectifs est d'économiser de la mémoire ou des cycles en éliminant les utilisations inutiles (c'est-à-dire les écritures / lectures) des membres.
L'essentiel est que «si le compilateur peut prouver dans le cadre d'une fonction (y compris les fonctions qui y étaient incorporées) que le membre inutilisé ne fait aucune différence quant au fonctionnement de la fonction (et à ce qu'elle renvoie), alors il y a de bonnes chances que la présence du membre n'entraîne aucune surcharge ".
Lorsque vous rendez les interactions d'une fonction avec le monde extérieur plus compliquées / peu claires pour le compilateur (prenez / retournez des structures de données plus complexes, par exemple std::vector<Foo>
, cachez la définition d'une fonction dans une unité de compilation différente, interdisez / désinciterez l'inlining, etc.) , il devient de plus en plus probable que le compilateur ne puisse pas prouver que le membre inutilisé n'a aucun effet.
Il n'y a pas de règles strictes ici car tout dépend des optimisations effectuées par le compilateur, mais tant que vous faites des choses triviales (comme le montre la réponse de YSC), il est très probable qu'aucune surcharge ne sera présente, tout en faisant des choses compliquées (par exemple, retourner a std::vector<Foo>
d'une fonction trop grande pour l'inlining) entraînera probablement la surcharge.
Pour illustrer ce point, considérons cet exemple :
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];
}
Nous faisons des choses non triviales ici (prendre des adresses, inspecter et ajouter des octets à partir de la représentation d'octets ) et pourtant l'optimiseur peut comprendre que le résultat est toujours le même sur cette plate-forme:
test(): # @test()
mov eax, 7
ret
Non seulement les membres de Foo
n'occupaient aucune mémoire, mais Foo
ils n'ont même pas vu le jour! S'il y a d'autres utilisations qui ne peuvent pas être optimisées, par exemple, cela sizeof(Foo)
peut avoir de l'importance - mais uniquement pour ce segment de code! Si toutes les utilisations pouvaient être optimisées de cette manière, l'existence de par exemple var3
n'influencait pas le code généré. Mais même s'il est utilisé ailleurs, test()
resterait optimisé!
En bref: chaque utilisation de Foo
est optimisée indépendamment. Certains peuvent utiliser plus de mémoire en raison d'un membre inutile, d'autres non. Consultez le manuel de votre compilateur pour plus de détails.