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;
}
我正在尝试编写一些代码,在其中我可以在堆栈上创建一个基 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;
}