2017-04-21 30 views
-2

我想寫一些代碼,我可以在堆棧上創建一個基類,並通過調用該基類來修改vtable。MSVC內嵌虛函數調用時,它不應該

class Base 
{ 
    public: 
     void initAs(int version); 
     virtual int foo() { assert(false); return 0; } 
}; 

class A : public Base 
{ 
public: 
    virtual int foo() { return 1; } 
}; 

void Base::initAs(int version) 
{ 
    switch(version) 
    { 
    case 1: 
     new (this) A(); 
     break; 
    default: 
     break; 
    } 
} 

int main() 
{ 
    Base x; 
    int version = 1; 
    x.initAs(version); 
    int v = x.foo(); 
    assert(v == 1); 
    return 0; 
} 

我運行到),其中x.foo()內聯調用基地:: foo的(問題,而不是A :: foo的()。我檢查了反彙編,並沒有vtable解決方案。編譯器決定內聯該虛函數,即使它應該能夠看到指向x的內存正在被修改。我怎樣才能停止編譯器(MSVC 14)內聯函數調用x.foo()?

編輯: 我不是在尋找意見或答案如何undefined這種行爲是。據我所知,我應該期望x.foo()應該通過整個虛擬函數調用堆棧,因爲它被標記爲虛函數,並且我無法完全限定函數名(akaxBase :: foo( ))。我在代碼中有其他地方可以工作:

class Container 
{ 
private: 
    Base x; 
    Container(); 
    void foo(); 
}; 

Container::Container() { x.initAs(1); } 
void Container::foo() { assert(x.foo() == 1; } // This call is correct A::foo() 

編譯器中的差異在哪裏,我該如何關閉它?

+5

這是未定義的行爲。 –

+2

編譯器正在做它應該做的事情。你的代碼有未定義的行爲,你的期望是無效的。 –

+0

這標記爲未定義的行爲在哪裏?你能給我一個鏈接嗎? –

回答

2

我無法發表評論,但我沒有親自嘗試過,但我想進行以下觀察 - 您可能無法強制編譯器不內聯函數。編譯器在內聯時出現了不一致,即使是關鍵字inline也不會強制它內聯(它更像是一個提示,它也使該函數對其他編譯單元不可見),並且關鍵字inline的缺失不會影響內聯這意味着它不能內聯。這強烈地表明,你所能做的最好的就是試圖繞過它。

我自己並沒有嘗試過這一個(如果它工作,它會有點奇怪,但如果我們要相信它應該的評論..),但想法是,現在你有一個參考,並且解決它們需要虛擬查找(yada)。

int main() 
{ 
    Base x_on_stack; 
    Base &x = x_on_stack; 
    int version = 1; 
    x.initAs(version); 
    int v = x.foo(); 
    assert(v == 1); 
    return 0; 
} 
+0

謝謝。我會說,做你的引用解決方法,甚至做(&x_on_stack) - > foo()將實現我想要的行爲。 __declspec(noinline)也不起作用。一位同事認爲編譯器中的內存別名功能被破壞,導致這種預優化。我現在正在調查clang如何執行這個例子。 –

相關問題