2017-07-02 225 views
12

看看這段代碼:編譯器爲什麼不優化?

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是常量,它不構造後更改。因此,理論上,在callInitAcallInitB中,編譯器在內聯~Object()時知道m_initType的值。但是,gcc和叮噹fails to apply這個優化,並且都檢查了值m_initType

這是爲什麼?有沒有針對這種優化的語言規則,或編譯器是不是做這種優化?

(這個問題是密切相關的this,但它是一個更具體的問題,我希望我能得到這個答案)

+0

'Init g; void somefunction(Object object){object。〜Object();新(&對象)對象(g);}'也許? –

+4

這是一個小得多的例子,我認爲它演示了同樣的問題:https://godbolt.org/g/zTyctM - 如果你註釋掉'somefunction',那麼整個事情就會被優化,但是'somefunction'被稱爲編譯器生成一個「不可能發生」的檢查。 –

+2

MSVC++應用此優化。有些是,有些則不是,優化者並不是平等的。 –

回答

0

對象的析構函數是不是在你的例子內聯,你有2個調用,其中在一個m_initType中是1而在另一箇中是0.因此編譯器必須支持這兩個版本。 此外,我想你的實際代碼比你的例子更復雜一點,所以編譯器可能會認爲內聯整個析構函數代碼比單獨使用'if'的通用版本更昂貴。

+0

你可以在析構函數前寫'inline'並用'g ++ -O3 -Winline -Werror'編譯 - 我想你會發現它不會改變任何東西。 –

+1

我想你已經誤解了我的問題。對象析構函數**是**內聯的,因爲它可以從反彙編中看出。當然,我的代碼更復雜,但是這個缺少的優化確實發生在我發佈的代碼中。 – geza

+0

@JohnZwinck我無法猜測暗示了什麼。我只回答無論我看到問題:)優化是自願的,並不是由標準強制執行。 '內聯'相同 - 你可以儘可能多地寫,但根據標準,它只是建議。因此,如果編譯器沒有看到任何好處,可以自由地忽略「內聯」。 – Noname

3

要回答是否有一個禁止這種優化的語言中的任何規則,這是我拿

[dcl.type.cv]

除任何類成員聲明可變可以修改的,任何企圖在其生命週期中修改一個const對象會導致未定義的行爲。

所以理論上,優化器可以安全地假定m_initType在初始化後永遠不會改變。這當然可以用來推斷~Object中的分支是否會在編譯時進行。

也就是說,只要觀察到的行爲保持不變,優化器可以自由地做任何事情,他們也可以自由忽略const。爲了讓優化器變得更復雜,混合中有一個前向聲明但未定義的函數,優化器可能會在這之後放棄對該信息進行任何有用的操作。

Comparison of defined vs undefined function

如果函數定義以後,gcc和鐺既優化了所有一切。但請注意,在這種特殊情況下,即使沒有任何const,他們也會still do this

This post可能有興趣

+1

感謝您的回答。是的,如果定義對編譯器可見,它可以進行通常的優化。我編輯了我的問題,明確指出'somefunction'是故意沒有定義的。 – geza

+0

如果'm_initType'不是一個對象,這個引用是不夠的...(該標準有一個相當有限的對象定義) –