Может ли компилятор оптимизировать данные константного класса вне класса?
Я знаю, что постоянные переменные вне классов могут быть оптимизированы компилятором непосредственно в вызовы функций, но законно ли компилятору делать то же самое для постоянных переменных класса?
Если существует класс, объявленный следующим образом:
class A {
public:
const int constVar;
//other, modifiable variables
A(int val): constVar(val) {
//code to initialize modifiable variables
}
};
И я создаю экземпляр A и вызываю функцию следующим образом:
A obj(-2);
int absoluteVal = std::abs(A.constVar);
Может ли компилятор сделать это вместо этого и сделать класс sizeof(int)
меньше?:
A obj();
int absoluteVal = std::abs(-2);
3 ответа:
Компилятор может свободно выдавать любой код, который сохраняет "наблюдаемое поведение" программы (есть исключение с конструктором копирования, которое может быть устранено, даже если оно имеет наблюдаемое поведение, но оно не применяется здесь). Это называется как если бы Правило
struct X { int x; }; auto foo() { X x{24}; return x.x; }
Любой приличный компилятор оптимизирует вышесказанное до этого:
Как вы можете видеть, это не имеет ничего общего с постоянством (ну, почти), просто с наблюдаемым поведением. Вы можете попробовать и поиграть с добавление кода в сложность и посмотреть, насколько умный компилятор в выяснении он может удалить код, связанный с экземпляром класса. Подсказка: это очень умно.foo(): # @foo() mov eax, 24 ret
Мне не совсем ясно, что вы имеете в виду под этим:Можно ли компилятору сделать это вместо этого и сделать класс sizeof (int) меньше?:
Я могу сказать вам, что: для типа
X
и Объектаx
такого типаsizeof(x)
Всегда= sizeof(X)
независимо от экземпляров класса. Другими словами, размер класса определяется, когда класс определен,и как таковой он не зависит от возможных экземпляров или отсутствия. Размер класса-это сумма всех размеров его нестатических элементов данных плюс заполнение. Заполнение определяется реализацией и обычно может быть несколько контролируемым (например, упакованные структуры). Так что нет, размер класса никогда не может быть меньше суммы размеров всех его нестатических несвязанных элементов данных.
Было бы совершенно законно для компилятора генерировать
int absoluteVal = 2;
Если
abs
не имеет побочных эффектов. Все это зависит от" наблюдаемого поведения " (правило as-if). Если вы не можете сказать извне, что компилятор произвел некоторое преобразование, то это законно для компилятора, чтобы сделать это преобразование.
Оптимизация кода и компоновка памяти объектов не подчиняются одним и тем же правилам
Стандарт C++ утверждает следующее о расположении памяти объектов :
1.8/2: объекты могут содержать другие объекты, называемые подобъектами. Подобъект может быть подобъектом-членом, подобъектом базового класса или элемент массива. (...)
9.2/13: нестатические члены данных класса (не объединенного) с одинаковым управлением доступом распределяются так что более поздние члены имеют более высокие адреса внутри объекта класса. Порядок выделения нестатических члены данных с различным управлением доступом не указаны. Требования к выравниванию реализации могут привести к появлению двух соседних элементов не должны быть распределены сразу после друг друга; так могли бы требования к пространству для управления виртуальными функциями и виртуальной базой занятия.
Это гарантирует, что нестатические члены const (которые являются членами данных, даже если они являются const) содержатся внутри объекта. Поэтому компилятору не разрешается уменьшать размер объекта.
Однако компилятор уполномочен выполнять оптимизацию кода , такую как постоянное распространение и устранение мертвого кода, переупорядочивание и т. д. до тех пор, пока наблюдаемое поведение не изменяется:1.9/5: соответствующая реализация, выполняющая хорошо сформированную программу, должна производить то же наблюдаемое поведение, что и одна из возможных расстрелы населения соответствующий экземпляр абстрактной машины с та же программа и тот же ввод. (...)
Поэтому, если ваш член const не является ни
volatile
, Ниatomic<>
, компилятор может очень хорошо генерироватьA obj(); // size not touched. And const member will be initialized if needed int absoluteVal = 2; // constant propagation + inlining (the object is not even accessed)
Дополнительная информация
Вот пример, где объект не может быть оптимизирован:
A obj(-2); // object is constructed int absoluteVal = std::abs(obj.constVar); // will be optimized a way into = 2 std::cout<<absoluteVal<<std::endl; size_t lo = sizeof(obj); std::cout<<lo<<std::endl; std::cout.write((char*)&obj, lo); // obj is written to a stream // and output of content at &obj adress is observable behavior
Вы можете посмотреть онлайн результаты оптимизатора : несмотря на то, что вычисление
absoluteVal
оптимизируется, объект создается во всей своей длине и его константа инициализируется :Это происходит потому, что наблюдаемое поведение записи этого тривиально копируемого объекта в поток требует, чтобы каждый байт объекта соответствовал ожиданиям.... mov esi, 2 ; this is absoluteVal calculation mov DWORD PTR [rsp+12], -2 ; the const in [rsp+12] object is nevertheless initialized ... lea rsi, [rsp+12] ; the address of the object mov edx, 4 ; and its length ... ; are used to call cout.write() call std::basic_ostream<char, std::char_traits<char> >::write(char const*, long)