MSVC 在不应该的时候内联虚函数调用

MSVC inlines virtual function call when it shouldn't

我正在尝试编写一些代码,在其中我可以在堆栈上创建一个基 class 并通过调用该基 class 来修改 vtable class。

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() 应该遍历整个虚函数调用堆栈,因为它被标记为虚函数,而且我无处尝试完全限定函数名称(a.k.a.x.Base::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()

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

我不能发表评论,我自己也没有试验过这些东西,但我想做以下观察——你可能无法强制编译器不内联函数。编译器在内联方面是出了名的不一致,即使是 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;
}