È importante rendersi conto che il codice prodotto dal compilatore non ha alcuna conoscenza effettiva delle strutture dati (perché una cosa del genere non esiste a livello di assembly) e nemmeno l'ottimizzatore. Il compilatore produce solo codice per ogni funzione , non strutture di dati .
Ok, scrive anche sezioni di dati costanti e simili.
Sulla base di ciò, possiamo già dire che l'ottimizzatore non "rimuoverà" o "eliminerà" i membri, perché non restituisce strutture di dati. Produce codice , che può o non può utilizzare i membri, e tra i suoi obiettivi c'è il risparmio di memoria o cicli eliminando usi inutili (cioè scritture / letture) dei membri.
L'essenza è che "se il compilatore può provare nell'ambito di una funzione (comprese le funzioni che sono state integrate in essa) che il membro inutilizzato non fa differenza per come opera la funzione (e cosa restituisce), allora è probabile che la presenza del membro non causa overhead ".
Man mano che si rendono le interazioni di una funzione con il mondo esterno più complicate / poco chiare per il compilatore (prendere / restituire strutture di dati più complesse, ad es. std::vector<Foo>
, nascondere la definizione di una funzione in una diversa unità di compilazione, proibire / disincentivare l'inlining ecc.) , diventa sempre più probabile che il compilatore non possa provare che il membro inutilizzato non ha alcun effetto.
Non ci sono regole rigide qui perché tutto dipende dalle ottimizzazioni effettuate dal compilatore, ma finché fai cose banali (come mostrato nella risposta di YSC) è molto probabile che non sia presente alcun overhead, mentre fare cose complicate (ad es. a std::vector<Foo>
da una funzione troppo grande per l'inlining) probabilmente incorrerà in un sovraccarico.
Per illustrare il punto, considera questo esempio :
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];
}
Facciamo cose non banali qui (prendiamo indirizzi, ispezioniamo e aggiungiamo byte dalla rappresentazione dei byte ) e tuttavia l'ottimizzatore può capire che il risultato è sempre lo stesso su questa piattaforma:
test(): # @test()
mov eax, 7
ret
Non solo i membri di Foo
non occupavano alcuna memoria, a Foo
non sono nemmeno nati! Se ci sono altri usi che non possono essere ottimizzati, ad esempio sizeof(Foo)
potrebbe essere importante, ma solo per quel segmento di codice! Se tutti gli usi potessero essere ottimizzati in questo modo, l'esistenza di eg var3
non influenza il codice generato. Ma anche se fosse utilizzato altrove, test()
rimarrebbe ottimizzato!
In breve: ogni utilizzo di Foo
è ottimizzato in modo indipendente. Alcuni potrebbero utilizzare più memoria a causa di un membro non necessario, altri no. Consultare il manuale del compilatore per maggiori dettagli.