Почему компиляторы не оптимизируют это?
Посмотрите на этот код:
struct Data {
};
struct Init {
Data *m_data;
Init() : m_data(new Data) { }
~Init() {
delete m_data;
}
};
class Object {
private:
const int m_initType;
Data *m_data;
public:
Object(const Init &init) : m_initType(0), m_data(init.m_data) { }
Object(Init &&init) : m_initType(1), m_data(init.m_data) { init.m_data = nullptr; }
~Object() {
if (m_initType==1) {
delete m_data;
}
}
};
void somefunction(const Object &object); // it is intentionally not defined
void callInitA() {
Init x;
somefunction(x);
}
void callInitB() {
somefunction(Init());
}
Поскольку Object::m_initType
- const, он не изменяется после конструктора. Итак, теоретически, в callInitA
и в callInitB
компилятор знает значение m_initType
, когда он встраивает ~Object()
. Однако gcc и clang не применяют эту оптимизацию, и оба проверяют значение m_initType
.
Почему? Есть ли какое-то языковое правило против этой оптимизации, или компиляторы просто не делают такого рода оптимизацию?
(Этот вопрос тесно связан с этим, но это более конкретный вопрос, я надеюсь, что смогу получить ответ для этого)
Ответы
Ответ 1
Чтобы ответить, есть ли какие-либо правила на языке, который запрещает такую оптимизацию, здесь я беру
Из [dcl.type.cv]
За исключением того, что любой член класса, объявленный mutable, может быть изменен, любая попытка изменить объект const во время его жизни приводит к поведению undefined.
И поэтому теоретически оптимизатор может спокойно предположить, что m_initType
никогда не изменится после инициализации. Разумеется, это можно использовать, чтобы определить, будет ли ветвь в ~Object
взята во время компиляции.
Тем не менее, оптимизаторы могут ничего делать, пока наблюдаемое поведение остается неизменным, поэтому они также могут игнорировать const
. Чтобы сделать вещи более сложными для оптимизатора, в миксе есть заявленная, но не определенная функция, оптимизатор, возможно, просто отказался после этого сделать что-нибудь полезное с информацией.
Сравнение определенной функции vs undefined
Если функция определена позже, gcc и clang обе оптимизируют все. Обратите внимание, что в этом конкретном случае они все еще делают это даже без const
.
Этот пост может представлять интерес
Ответ 2
Деструктор объекта не указан в вашем примере, и у вас есть 2 вызова, в которых один m_initType равен 1, а в другом - 0. Поэтому компилятор должен поддерживать обе версии.
Кроме того, я полагаю, что ваш фактический код несколько сложнее, чем ваш пример, поэтому компилятор может решить, что встроенный код всего деструктора дороже, чем сохранение общей версии с одиночным "if" внутри.