É importante perceber que o código que o compilador produz não tem nenhum conhecimento real de suas estruturas de dados (porque tal coisa não existe no nível de montagem), e nem o otimizador. O compilador produz apenas código para cada função , não estruturas de dados .
Ok, ele também grava seções de dados constantes e tal.
Com base nisso, já podemos dizer que o otimizador não irá "remover" ou "eliminar" membros, pois não produz estruturas de dados. Ele produz código , que pode ou não usar os membros, e entre seus objetivos está a economia de memória ou ciclos, eliminando usos inúteis (ou seja, gravações / leituras) dos membros.
A essência disso é que "se o compilador pode provar dentro do escopo de uma função (incluindo funções que foram embutidas nela) que o membro não utilizado não faz diferença em como a função opera (e o que ela retorna), então as chances são boas de que a presença do membro não causa sobrecarga ”.
Conforme você torna as interações de uma função com o mundo externo mais complicadas / confusas para o compilador (pegar / retornar estruturas de dados mais complexas, por exemplo std::vector<Foo>
, a , ocultar a definição de uma função em uma unidade de compilação diferente, proibir / desincentivar inlining etc.) , torna-se cada vez mais provável que o compilador não consiga provar que o membro não utilizado não tem efeito.
Não há regras rígidas aqui porque tudo depende das otimizações que o compilador faz, mas contanto que você faça coisas triviais (como mostrado na resposta de YSC) é muito provável que nenhuma sobrecarga esteja presente, enquanto fazer coisas complicadas (por exemplo, retornar a std::vector<Foo>
de uma função muito grande para inlining) provavelmente incorrerá na sobrecarga.
Para ilustrar o ponto, considere este exemplo :
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];
}
Nós fazemos coisas não triviais aqui (pegar endereços, inspecionar e adicionar bytes da representação de bytes ) e ainda assim o otimizador pode descobrir que o resultado é sempre o mesmo nesta plataforma:
test(): # @test()
mov eax, 7
ret
Não só os membros de Foo
não ocuparam nenhuma memória, Foo
como nem mesmo passaram a existir! Se houver outros usos que não podem ser otimizados, por exemplo, sizeof(Foo)
pode importar - mas apenas para aquele segmento de código! Se todos os usos pudessem ser otimizados dessa forma, a existência de eg var3
não influencia o código gerado. Mas mesmo se for usado em outro lugar, test()
permanecerá otimizado!
Resumindo: cada uso de Foo
é otimizado de forma independente. Alguns podem usar mais memória por causa de um membro desnecessário, outros não. Consulte o manual do compilador para obter mais detalhes.