Важно понимать, что код, который генерирует компилятор, не имеет реальных знаний о ваших структурах данных (потому что такого не существует на уровне сборки), равно как и оптимизатор. Компилятор создает только код для каждой функции , а не структур данных .
Хорошо, он также записывает постоянные секции данных и тому подобное.
Исходя из этого, мы уже можем сказать, что оптимизатор не будет "удалять" или "исключать" элементы, поскольку он не выводит структуры данных. Он выводит код , который может или не может использовать членов, и среди его целей - экономия памяти или циклов за счет исключения бессмысленных использования (т.е. записи / чтения) членов.
Суть этого в том, что "если компилятор может доказать в рамках функции (включая функции, которые были встроены в него), что неиспользуемый элемент не имеет значения для того, как работает функция (и то, что он возвращает), тогда велики шансы, что присутствие члена не вызывает накладных расходов ".
По мере того, как вы делаете взаимодействие функции с внешним миром более сложным / неясным для компилятора (принимать / возвращать более сложные структуры данных, например, std::vector<Foo>
, скрыть определение функции в другом модуле компиляции, запретить / отменяет включение строк и т. д.), становится все более вероятным, что компилятор не сможет доказать, что неиспользованный элемент не имеет никакого эффекта.
Здесь нет жестких правил, потому что все зависит от оптимизаций, которые выполняет компилятор, но, пока вы делаете тривиальные вещи (такие как показано в ответе YSC), очень вероятно, что никаких накладных расходов не будет, тогда как делать сложные вещи (например, возврат std::vector<Foo>
из функции, слишком большой для встраивания), вероятно, потребует дополнительных затрат.
Чтобы проиллюстрировать это, рассмотрим этот пример :
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];
}
Здесь мы делаем нетривиальные вещи (берём адреса, проверяем и добавляем байты из байтового представления ), и все же оптимизатор может выяснить, что результат на этой платформе всегда одинаков:
test(): # @test()
mov eax, 7
ret
Мало того, что члены Foo
не занимали никакой памяти, Foo
даже не появился! Если есть другие способы использования, которые нельзя оптимизировать, например, sizeof(Foo)
может иметь значение - но только для этого сегмента кода! Если бы все виды использования могли быть оптимизированы таким образом, то, например, существование, например, var3
не влияет на сгенерированный код. Но даже если он используется где-то еще, test()
останется оптимизированным!
Вкратце: Каждое использование Foo
оптимизируется независимо. Некоторые могут использовать больше памяти из-за ненужного члена, некоторые - нет. Обратитесь к руководству вашего компилятора для получения более подробной информации.