MSVC在不应该的情况下内联虚拟函数调用

时间:2017-04-21 21:38:04

标签: c++ visual-c++ virtual

我试图编写一些代码,我可以在堆栈上创建基类,并通过调用该基类来修改vtable。

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()以调用Base :: foo()而不是A :: foo()的问题。我检查了反汇编,没有vtable分辨率。编译器决定内联该虚函数,即使它应该能够看到指向x的内存正在被修改。如何阻止编译器(MSVC 14)内联函数调用x.foo()?

编辑: 我不是在寻找关于未定义此行为的评论或答案。据我所知,我应该期望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()

编译器中的差异在哪里?如何将其关闭?

1 个答案:

答案 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;
}