Почему компиляторы не оптимизируют это?

Посмотрите на этот код:

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" внутри.