C ++ Можно ли оптимизировать данные константного класса вне класса компилятором?

Я знаю, что постоянные переменные вне классов могут быть оптимизированы напрямую в вызовы функций компилятором, но допустимо ли для компилятора делать то же самое для переменных постоянных классов?

Если есть класс, объявленный так:

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) независимо от экземпляров класса. Другими словами, размер класса определяется, когда класс определен, и, как таковой, на него не влияют возможные реализации или его отсутствие. Размер класса является суммой всех размеров его нестатических элементов данных плюс заполнение. Заполнение определяется реализацией и может обычно управляться (например, упакованными структурами). Так что нет, размер класса никогда не может быть меньше суммы размеров всех его нестатических не ссылочных элементов данных.

9

Другие решения

Для компилятора было бы совершенно законно сгенерировать

int absoluteVal = 2;

Если abs не имеет побочных эффектов. Все зависит от «наблюдаемого поведения» ( как будто правило). если ты не может сказать извне, что компилятор выполнил какое-то преобразование, тогда это допустимо для компилятора.

2

Оптимизация кода и структура памяти объекта не подчиняются тем же правилам

Стандарт C ++ гласит следующее о расположение в памяти объектов:

1.8 / 2: Объекты могут содержать другие объекты, называемые подобъектами. Подобъект может быть подобъектом-членом, подобъектом базового класса или
элемент массива. (…)

9.2 / 13: Нестатические данные-члены (не объединяющего) класса с одинаковым контролем доступа выделено так что более поздние члены имеют выше
адреса внутри объекта класса. Порядок выделения нестатических
члены данных с разным контролем доступа не определены.
Требования выравнивания реализации могут привести к двум соседним элементам
не распределяются сразу после друг друга; так может
требования к пространству для управления виртуальными функциями и виртуальной базой
классы.

Это гарантирует, что нестатические члены const (которые являются членами данных, даже если они являются const) содержатся внутри объекта. Таким образом, компилятору не разрешается сокращать размер объекта.

Тем не менее, компилятор имеет право выполнять оптимизация кода такие как постоянное распространение и устранение мертвого кода, переупорядочение и т. д., если наблюдаемое поведение не изменяется:

1.9 / 5: Соответствующая реализация, выполняющая правильно сформированную программу, должна производить то же наблюдаемое поведение, что и одно из возможных
выполнения соответствующего экземпляра абстрактной машины с
та же программа и тот же вход. (…)

Так что если ваш постоянный член не 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)

Это потому, что наблюдаемое поведение записи этого тривиально копируемого объекта в поток требует, чтобы каждый байт объекта соответствовал ожиданиям.

2
По вопросам рекламы [email protected]