Важно понимать, что код, создаваемый компилятором, не имеет фактических сведений о ваших структурах данных (потому что таких вещей не существует на уровне сборки), как и оптимизатор. Компилятор создает только код для каждой функции , но не структуры данных .
Хорошо, он также записывает постоянные разделы данных и тому подобное.
Исходя из этого, мы уже можем сказать, что оптимизатор не будет «удалять» или «исключать» члены, потому что он не выводит структуры данных. Он выводит код , который может использовать или не использовать элементы, и среди его целей экономия памяти или циклов за счет исключения бессмысленного использования (например, записи / чтения) членов.
Суть этого в том, что «если компилятор может доказать в рамках функции (включая функции, которые были встроены в нее), что неиспользуемый член не имеет значения для того, как функция работает (и что она возвращает), то велики шансы, что присутствие члена не вызывает накладных расходов ».
По мере того, как вы делаете взаимодействие функции с внешним миром более сложным / неясным для компилятора (принимать / возвращать более сложные структуры данных, например std::vector<Foo>
, скрывать определение функции в другом модуле компиляции, запрещать / препятствовать встраиванию и т. Д.) , становится все более и более вероятным, что компилятор не может доказать, что неиспользуемый член не действует.
Здесь нет жестких правил, потому что все зависит от оптимизаций, которые делает компилятор, но пока вы делаете тривиальные вещи (например, как показано в ответе YSC), очень вероятно, что никаких накладных расходов не будет, тогда как выполнение сложных вещей (например, возврат a 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
оптимизируется независимо. Некоторые могут использовать больше памяти из-за ненужного элемента, некоторые - нет. Для получения более подробной информации обратитесь к руководству вашего компилятора.