Разрешено ли компилятору оптимизировать члены закрытых данных?

Dec 08 2020

Если компилятор может доказать, что (частный) член класса никогда не используется, в том числе потенциальными друзьями, позволяет ли стандарт компилятору удалить этот член из памяти класса?

Само собой разумеется, что это невозможно для защищенных или общедоступных членов во время компиляции, но могут быть обстоятельства, когда это возможно в отношении частных данных-членов для создания такого доказательства.


Связанные вопросы:

  • За кулисами публичного, частного и защищенного (возник вопрос)
  • Разрешено ли компилятору C ++ оптимизировать локальные объекты, на которые нет ссылок (об автоматических объектах)
  • Всегда ли статическая переменная использует память? (о статических объектах)

Ответы

31 PeterCordes Dec 08 2020 at 23:19

Возможно теоретически (вместе с неиспользуемыми общедоступными членами), но не с той экосистемой компилятора, к которой мы привыкли (нацеленность на фиксированный ABI, который может связывать отдельно скомпилированный код). Удаление неиспользуемых элементов может быть выполнено только с помощью оптимизации всей программы, которая запрещает отдельные библиотеки 1 .

Возможно, потребуется согласовать другие единицы компиляции sizeof(foo), но это не было бы тем, что вы могли бы получить из a, .hесли бы это зависело от проверки того, что никакая реализация поведения функции-члена не зависит от каких-либо закрытых членов.

Помните, что C ++ действительно определяет только одну программу, а не способ создания библиотек. На языке ISO C ++ определяет совместят со стилем реализации мы привыкли (конечно), но реализация , которые принимают все .cppи .hфайлы сразу и производят один автономный нерастяжимой исполняемым возможна.

Если вы достаточно ограничите реализацию (без фиксированного ABI), становится возможным агрессивное применение правила «как если бы» во всей программе.


Сноска 1: я собирался добавить « или каким-то образом экспортировать информацию о размере в другой компилируемый код », чтобы разрешить библиотеки, если компилятор уже мог видеть определения для каждой функции-члена, объявленной в классе. Но ответ @ PasserBy указывает на то, что отдельно скомпилированная библиотека может быть тем, что использует объявленные частные члены способами, которые в конечном итоге вызывают видимые извне побочные эффекты (например, ввод-вывод). Так что нам придется полностью их исключить.

При этом публичные и частные члены эквивалентны для целей такой оптимизации.

17 KonradRudolph Dec 08 2020 at 22:26

Если компилятор может доказать, что (частный) член класса никогда не используется

Компилятор не может этого доказать, потому что закрытые члены могут использоваться в других единицах компиляции. Конкретно, это возможно в контексте указателя на член в аргументе шаблона согласно [temp.spec] / 6 стандарта, как первоначально описано Йоханнесом Шаубом .

Итак, подведем итоги: нет, компилятор не должен оптимизировать частные члены данных не больше, чем публичные или защищенные члены (в соответствии с правилом «как если бы»).

11 PasserBy Dec 08 2020 at 22:30

Нет, потому что вы можете подорвать систему контроля доступа на законных основаниях .

class A
{
    int x;
};

auto f();

template<auto x>
struct cheat
{
    friend auto f() { return x; }
};

template struct cheat<&A::x>;  // see [temp.spec]/6

int& foo(A& a)
{
    return a.*f();  // returns a.x
}

Учитывая, что компилятор должен исправить ABI при Aпервом использовании, и что он никогда не может знать, может ли какой-то будущий код получить доступ x, он должен исправить память, Aчтобы содержать x.